From 25597bdfc148e91e27678ec30efa52f4fc8c164f Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Wed, 2 Oct 2019 15:20:57 -0700 Subject: [PATCH 0001/3164] Export of internal Abseil changes -- 3e60f355db5afd7a864591d81a6c383b6c0a0780 by Samuel Benzaquen : Internal change PiperOrigin-RevId: 272531442 -- 6d189240b8cebe3a390c730de491156d03049229 by Andy Getzendanner : Fix AtomicHook init-order fiasco under MSVC 2019. On this platform, constexpr static init sometimes happens after dynamic init =/. When it does, we should not zero hook_ (overwriting the value written there by dynamic init); instead we should leave it alone. This works even when constexpr static init goes first since all uses of AtomicHook should have static storage duration and be zero-initialized. https://developercommunity.visualstudio.com/content/problem/336946/class-with-constexpr-constructor-not-using-static.html PiperOrigin-RevId: 272525226 -- d01b14fc06bc75b41c51976ed32e7c304ea1aab7 by Abseil Team : exclude emscripten from running tests involving long doubles PiperOrigin-RevId: 272497628 GitOrigin-RevId: 3e60f355db5afd7a864591d81a6c383b6c0a0780 Change-Id: I3c8a8f5acaf7652a06ef40cf028ef5d2e142f81b --- absl/base/BUILD.bazel | 14 ++++++++ absl/base/CMakeLists.txt | 15 +++++++++ absl/base/internal/atomic_hook.h | 26 ++++++++++----- absl/base/internal/atomic_hook_test.cc | 24 ++++++++++++++ absl/base/internal/atomic_hook_test_helper.cc | 29 +++++++++++++++++ absl/base/internal/atomic_hook_test_helper.h | 32 +++++++++++++++++++ absl/container/internal/hashtablez_sampler.cc | 20 +++++++++++- absl/container/internal/hashtablez_sampler.h | 2 +- ...ashtablez_sampler_force_weak_definition.cc | 5 +-- absl/random/exponential_distribution_test.cc | 4 +++ .../internal/iostream_state_saver_test.cc | 2 ++ absl/random/uniform_real_distribution_test.cc | 5 +++ 12 files changed, 166 insertions(+), 12 deletions(-) create mode 100644 absl/base/internal/atomic_hook_test_helper.cc create mode 100644 absl/base/internal/atomic_hook_test_helper.h diff --git a/absl/base/BUILD.bazel b/absl/base/BUILD.bazel index 57caa0656e5..1e1f0d2de31 100644 --- a/absl/base/BUILD.bazel +++ b/absl/base/BUILD.bazel @@ -207,6 +207,19 @@ cc_library( ], ) +cc_library( + name = "atomic_hook_test_helper", + testonly = 1, + srcs = ["internal/atomic_hook_test_helper.cc"], + hdrs = ["internal/atomic_hook_test_helper.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":atomic_hook", + ":core_headers", + ], +) + cc_test( name = "atomic_hook_test", size = "small", @@ -215,6 +228,7 @@ cc_test( linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":atomic_hook", + ":atomic_hook_test_helper", ":core_headers", "@com_google_googletest//:gtest_main", ], diff --git a/absl/base/CMakeLists.txt b/absl/base/CMakeLists.txt index 2f11ef86556..51705a5a102 100644 --- a/absl/base/CMakeLists.txt +++ b/absl/base/CMakeLists.txt @@ -254,6 +254,19 @@ absl_cc_test( gtest_main ) +absl_cc_library( + NAME + atomic_hook_test_helper + SRCS + "internal/atomic_hook_test_helper.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::atomic_hook + absl::core_headers + TESTONLY +) + absl_cc_test( NAME atomic_hook_test @@ -262,8 +275,10 @@ absl_cc_test( COPTS ${ABSL_TEST_COPTS} DEPS + absl::atomic_hook_test_helper absl::atomic_hook absl::core_headers + gmock gtest_main ) diff --git a/absl/base/internal/atomic_hook.h b/absl/base/internal/atomic_hook.h index 803e9059761..09f763d0742 100644 --- a/absl/base/internal/atomic_hook.h +++ b/absl/base/internal/atomic_hook.h @@ -11,7 +11,6 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -// #ifndef ABSL_BASE_INTERNAL_ATOMIC_HOOK_H_ #define ABSL_BASE_INTERNAL_ATOMIC_HOOK_H_ @@ -23,8 +22,10 @@ #ifdef _MSC_FULL_VER #define ABSL_HAVE_WORKING_ATOMIC_POINTER 0 +#define ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT 0 #else #define ABSL_HAVE_WORKING_ATOMIC_POINTER 1 +#define ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT 1 #endif namespace absl { @@ -33,16 +34,17 @@ namespace base_internal { template class AtomicHook; -// AtomicHook is a helper class, templatized on a raw function pointer type, for -// implementing Abseil customization hooks. It is a callable object that -// dispatches to the registered hook. +// `AtomicHook` is a helper class, templatized on a raw function pointer type, +// for implementing Abseil customization hooks. It is a callable object that +// dispatches to the registered hook. Objects of type `AtomicHook` must have +// static or thread storage duration. // // A default constructed object performs a no-op (and returns a default // constructed object) if no hook has been registered. // // Hooks can be pre-registered via constant initialization, for example, -// ABSL_CONST_INIT static AtomicHook my_hook(DefaultAction); -// and then changed at runtime via a call to Store(). +// `ABSL_CONST_INIT static AtomicHook my_hook(DefaultAction);` +// and then changed at runtime via a call to `Store()`. // // Reads and writes guarantee memory_order_acquire/memory_order_release // semantics. @@ -57,12 +59,19 @@ class AtomicHook { // Constructs an object that by default dispatches to/returns the // pre-registered default_fn when no hook has been registered at runtime. -#if ABSL_HAVE_WORKING_ATOMIC_POINTER +#if ABSL_HAVE_WORKING_ATOMIC_POINTER && ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT explicit constexpr AtomicHook(FnPtr default_fn) : hook_(default_fn), default_fn_(default_fn) {} #else + // On MSVC, this function sometimes executes after dynamic initiazliation =(. + // If a non-zero `hook_` has been installed by a dynamic initializer, we want + // to preserve it. If not, `hook_` will be zero initialized and we have no + // need to set it to `kUninitialized`. + // https://developercommunity.visualstudio.com/content/problem/336946/class-with-constexpr-constructor-not-using-static.html explicit constexpr AtomicHook(FnPtr default_fn) - : hook_(kUninitialized), default_fn_(default_fn) {} + : /* hook_(deliberately omitted), */ default_fn_(default_fn) { + static_assert(kUninitialized == 0, "here we rely on zero-initialization"); + } #endif // Stores the provided function pointer as the value for this hook. @@ -158,6 +167,7 @@ class AtomicHook { }; #undef ABSL_HAVE_WORKING_ATOMIC_POINTER +#undef ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT } // namespace base_internal } // namespace absl diff --git a/absl/base/internal/atomic_hook_test.cc b/absl/base/internal/atomic_hook_test.cc index ecc80406b00..794072ee2fc 100644 --- a/absl/base/internal/atomic_hook_test.cc +++ b/absl/base/internal/atomic_hook_test.cc @@ -14,11 +14,15 @@ #include "absl/base/internal/atomic_hook.h" +#include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/base/attributes.h" +#include "absl/base/internal/atomic_hook_test_helper.h" namespace { +using ::testing::Eq; + int value = 0; void TestHook(int x) { value = x; } @@ -67,4 +71,24 @@ TEST(AtomicHookTest, WithDefaultFunction) { EXPECT_EQ(value, 2); } +ABSL_CONST_INIT int override_func_calls = 0; +void OverrideFunc() { override_func_calls++; } +static struct OverrideInstaller { + OverrideInstaller() { absl::atomic_hook_internal::func.Store(OverrideFunc); } +} override_installer; + +TEST(AtomicHookTest, DynamicInitFromAnotherTU) { + // MSVC 14.2 doesn't do constexpr static init correctly; in particular it + // tends to sequence static init (i.e. defaults) of `AtomicHook` objects + // after their dynamic init (i.e. overrides), overwriting whatever value was + // written during dynamic init. This regression test validates the fix. + // https://developercommunity.visualstudio.com/content/problem/336946/class-with-constexpr-constructor-not-using-static.html + EXPECT_THAT(absl::atomic_hook_internal::default_func_calls, Eq(0)); + EXPECT_THAT(override_func_calls, Eq(0)); + absl::atomic_hook_internal::func(); + EXPECT_THAT(absl::atomic_hook_internal::default_func_calls, Eq(0)); + EXPECT_THAT(override_func_calls, Eq(1)); + EXPECT_THAT(absl::atomic_hook_internal::func.Load(), Eq(OverrideFunc)); +} + } // namespace diff --git a/absl/base/internal/atomic_hook_test_helper.cc b/absl/base/internal/atomic_hook_test_helper.cc new file mode 100644 index 00000000000..fad7a89a6ff --- /dev/null +++ b/absl/base/internal/atomic_hook_test_helper.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/base/internal/atomic_hook_test_helper.h" + +#include "absl/base/attributes.h" +#include "absl/base/internal/atomic_hook.h" + +namespace absl { +namespace atomic_hook_internal { + +ABSL_CONST_INIT absl::base_internal::AtomicHook func(DefaultFunc); +ABSL_CONST_INIT int default_func_calls = 0; +void DefaultFunc() { default_func_calls++; } +void RegisterFunc(VoidF f) { func.Store(f); } + +} // namespace atomic_hook_internal +} // namespace absl diff --git a/absl/base/internal/atomic_hook_test_helper.h b/absl/base/internal/atomic_hook_test_helper.h new file mode 100644 index 00000000000..44ff780d6b9 --- /dev/null +++ b/absl/base/internal/atomic_hook_test_helper.h @@ -0,0 +1,32 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_BASE_ATOMIC_HOOK_TEST_HELPER_H_ +#define ABSL_BASE_ATOMIC_HOOK_TEST_HELPER_H_ + +#include "absl/base/internal/atomic_hook.h" + +namespace absl { +namespace atomic_hook_internal { + +using VoidF = void (*)(); +extern absl::base_internal::AtomicHook func; +extern int default_func_calls; +void DefaultFunc(); +void RegisterFunc(VoidF func); + +} // namespace atomic_hook_internal +} // namespace absl + +#endif // ABSL_BASE_ATOMIC_HOOK_TEST_HELPER_H_ diff --git a/absl/container/internal/hashtablez_sampler.cc b/absl/container/internal/hashtablez_sampler.cc index d03dd82e7fd..054e8981781 100644 --- a/absl/container/internal/hashtablez_sampler.cc +++ b/absl/container/internal/hashtablez_sampler.cc @@ -228,8 +228,26 @@ int64_t HashtablezSampler::Iterate( return dropped_samples_.load(std::memory_order_relaxed); } +static bool ShouldForceSampling() { + enum ForceState { + kDontForce, + kForce, + kUninitialized + }; + ABSL_CONST_INIT static std::atomic global_state{ + kUninitialized}; + ForceState state = global_state.load(std::memory_order_relaxed); + if (ABSL_PREDICT_TRUE(state == kDontForce)) return false; + + if (state == kUninitialized) { + state = AbslContainerInternalSampleEverything() ? kForce : kDontForce; + global_state.store(state, std::memory_order_relaxed); + } + return state == kForce; +} + HashtablezInfo* SampleSlow(int64_t* next_sample) { - if (kAbslContainerInternalSampleEverything) { + if (ABSL_PREDICT_FALSE(ShouldForceSampling())) { *next_sample = 1; return HashtablezSampler::Global().Register(); } diff --git a/absl/container/internal/hashtablez_sampler.h b/absl/container/internal/hashtablez_sampler.h index 4112175449e..53996bb9fa4 100644 --- a/absl/container/internal/hashtablez_sampler.h +++ b/absl/container/internal/hashtablez_sampler.h @@ -280,7 +280,7 @@ void SetHashtablezMaxSamples(int32_t max); // initialization of static storage duration objects. // The definition of this constant is weak, which allows us to inject a // different value for it at link time. -extern "C" const bool kAbslContainerInternalSampleEverything; +extern "C" bool AbslContainerInternalSampleEverything(); } // namespace container_internal } // namespace absl diff --git a/absl/container/internal/hashtablez_sampler_force_weak_definition.cc b/absl/container/internal/hashtablez_sampler_force_weak_definition.cc index 4ca6ffdadd7..984dce5dd99 100644 --- a/absl/container/internal/hashtablez_sampler_force_weak_definition.cc +++ b/absl/container/internal/hashtablez_sampler_force_weak_definition.cc @@ -20,8 +20,9 @@ namespace absl { namespace container_internal { // See hashtablez_sampler.h for details. -extern "C" ABSL_ATTRIBUTE_WEAK const bool - kAbslContainerInternalSampleEverything = false; +extern "C" ABSL_ATTRIBUTE_WEAK bool AbslContainerInternalSampleEverything() { + return false; +} } // namespace container_internal } // namespace absl diff --git a/absl/random/exponential_distribution_test.cc b/absl/random/exponential_distribution_test.cc index dc49044d3b7..f3cfd76442c 100644 --- a/absl/random/exponential_distribution_test.cc +++ b/absl/random/exponential_distribution_test.cc @@ -46,7 +46,11 @@ using absl::random_internal::kChiSquared; template class ExponentialDistributionTypedTest : public ::testing::Test {}; +#if defined(__EMSCRIPTEN__) +using RealTypes = ::testing::Types; +#else using RealTypes = ::testing::Types; +#endif // defined(__EMSCRIPTEN__) TYPED_TEST_CASE(ExponentialDistributionTypedTest, RealTypes); TYPED_TEST(ExponentialDistributionTypedTest, SerializeTest) { diff --git a/absl/random/internal/iostream_state_saver_test.cc b/absl/random/internal/iostream_state_saver_test.cc index 2ecbaac143d..722766d0ff6 100644 --- a/absl/random/internal/iostream_state_saver_test.cc +++ b/absl/random/internal/iostream_state_saver_test.cc @@ -272,6 +272,7 @@ TEST(IOStreamStateSaver, RoundTripDoubles) { } } +#if !defined(__EMSCRIPTEN__) TEST(IOStreamStateSaver, RoundTripLongDoubles) { // Technically, C++ only guarantees that long double is at least as large as a // double. Practically it varies from 64-bits to 128-bits. @@ -349,6 +350,7 @@ TEST(IOStreamStateSaver, RoundTripLongDoubles) { } } } +#endif // !defined(__EMSCRIPTEN__) TEST(StrToDTest, DoubleMin) { const char kV[] = "2.22507385850720138e-308"; diff --git a/absl/random/uniform_real_distribution_test.cc b/absl/random/uniform_real_distribution_test.cc index 597f0ee5efa..9f14d1c5898 100644 --- a/absl/random/uniform_real_distribution_test.cc +++ b/absl/random/uniform_real_distribution_test.cc @@ -54,7 +54,12 @@ namespace { template class UniformRealDistributionTest : public ::testing::Test {}; +#if defined(__EMSCRIPTEN__) +using RealTypes = ::testing::Types; +#else using RealTypes = ::testing::Types; +#endif // defined(__EMSCRIPTEN__) + TYPED_TEST_SUITE(UniformRealDistributionTest, RealTypes); TYPED_TEST(UniformRealDistributionTest, ParamSerializeTest) { From abea769b551f7a100f540967cb95debdb0080df8 Mon Sep 17 00:00:00 2001 From: Joe Sylve Date: Wed, 9 Oct 2019 16:07:57 -0500 Subject: [PATCH 0002/3164] Fix ABSL_HAVE_ALARM check on mingw (#341) * Fix ABSL_HAVE_ALARM check on mingw --- absl/base/config.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/absl/base/config.h b/absl/base/config.h index 1cb69b0f700..039b73d81f7 100644 --- a/absl/base/config.h +++ b/absl/base/config.h @@ -334,6 +334,11 @@ #define ABSL_HAVE_ALARM 1 #elif defined(_MSC_VER) // feature tests for Microsoft's library +#elif defined(__MINGW32__) +// mingw32 doesn't provide alarm(2): +// https://osdn.net/projects/mingw/scm/git/mingw-org-wsl/blobs/5.2-trunk/mingwrt/include/unistd.h +// mingw-w64 provides a no-op implementation: +// https://sourceforge.net/p/mingw-w64/mingw-w64/ci/master/tree/mingw-w64-crt/misc/alarm.c #elif defined(__EMSCRIPTEN__) // emscripten doesn't support signals #elif defined(__Fuchsia__) From e9f9000c7c80993cb589d011616b7a8016e42f4a Mon Sep 17 00:00:00 2001 From: Joe Sylve Date: Thu, 10 Oct 2019 15:51:23 -0500 Subject: [PATCH 0003/3164] Fix ABSL_WAITER_MODE detection for mingw (#342) * Fix ABSL_WAITER_MODE detection for mingw --- absl/synchronization/internal/waiter.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/absl/synchronization/internal/waiter.h b/absl/synchronization/internal/waiter.h index a3e3124e441..0757b91d79d 100644 --- a/absl/synchronization/internal/waiter.h +++ b/absl/synchronization/internal/waiter.h @@ -18,7 +18,9 @@ #include "absl/base/config.h" -#ifndef _WIN32 +#ifdef _WIN32 +#include +#else #include #endif @@ -40,7 +42,7 @@ #if defined(ABSL_FORCE_WAITER_MODE) #define ABSL_WAITER_MODE ABSL_FORCE_WAITER_MODE -#elif defined(_WIN32) +#elif defined(_WIN32) && _WIN32_WINNT >= _WIN32_WINNT_VISTA #define ABSL_WAITER_MODE ABSL_WAITER_MODE_WIN32 #elif defined(__linux__) #define ABSL_WAITER_MODE ABSL_WAITER_MODE_FUTEX From ab3552a18964e7063c8324f45b3896a6a20b08a8 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Tue, 15 Oct 2019 18:18:40 -0700 Subject: [PATCH 0004/3164] Export of internal Abseil changes -- f13697e3d33803f9667d124072da4f6dd8bfbf85 by Andy Soffer : Addressing https://github.com/abseil/abseil-cpp/issues/314, fixing CMakeLists.txt to reference ABSL_TEST_COPTS rather than ABSL_DEFAULT_COPTS. ABSL_TEST_COPTS should be preferred for all tests so that they are configured consistently (moreover, CMake should agree with Bazel). PiperOrigin-RevId: 274932312 -- c31c24a1fa6bb98136adf51ef37c0818ac366690 by Derek Mauro : Silence MSAN in the stack consumption test utility PiperOrigin-RevId: 274912950 -- 2412913c05a246cd527cd4c31452f126e9129f3a by CJ Johnson : Internal change PiperOrigin-RevId: 274847103 -- 75e984a93b5760873501b96ac3229ccfd955daf8 by Abseil Team : Reformat BUILD file to current standards. PiperOrigin-RevId: 274815392 -- a2780e085f1df1e4ca2c814a58c893d1b78a1d9c by Samuel Benzaquen : Fix invalid result regarding leading zeros in the exponent. PiperOrigin-RevId: 274808017 -- dd402e1cb5c4ebacb576372ae24bf289d729d323 by Samuel Benzaquen : Make string_view's relational operators constexpr when possible. PiperOrigin-RevId: 274807873 -- b4ef32565653a5da1cb8bb8d0351586d23519658 by Abseil Team : Internal rework. PiperOrigin-RevId: 274787159 -- 70d81971c5914e6785b8e8a9d4f6eb2655dd62c0 by Gennadiy Rozental : Internal rework. PiperOrigin-RevId: 274715557 -- 14f5b0440e353b899cafaaa15b53e77f98f401af by Gennadiy Rozental : Make deprecated statements about ParseFLag/UnparseFlag consistent in a file. PiperOrigin-RevId: 274668123 -- 2e85adbdbb92612e4d750bc34fbca3333128b42d by Abseil Team : Allow absl::c_equal to be used with arrays. This is achieved by allowing container size computation for arrays. PiperOrigin-RevId: 274426830 -- 219719f107226d328773e6cec99fb473f5d3119c by Gennadiy Rozental : Release correct extension interfaces to support usage of absl::Time and absl::Duration as ABSL_FLAG PiperOrigin-RevId: 274273788 -- 47a77f93fda23b69b4a6bdbd506fe643c69a5579 by Gennadiy Rozental : Rework of flags persistence/FlagSaver internals. PiperOrigin-RevId: 274225213 -- 7807be3fe757c19e3b0c487298387683d4c9f5b3 by Abseil Team : Switch reference to sdkddkver.h to lowercase, matching conventions used in the Windows SDK and other uses. This helps to avoid confusion on case-sensitive filesystems. PiperOrigin-RevId: 274061877 -- 561304090087a19f1d10f0475f564fe132ebf06e by Andy Getzendanner : Fix ABSL_WAITER_MODE detection for mingw Import of https://github.com/abseil/abseil-cpp/pull/342 PiperOrigin-RevId: 274030071 -- 9b3caac2cf202b9d440dfa1b4ffd538ac4bf715b by Derek Mauro : Support using Abseil with the musl libc implementation. Only test changes were required: * Workaround for a bug in sigaltstack() on musl * printf-style pointer formatting (%p) is implementation defined, so verify StrFromat produces something compatible * Fix detection of feenableexcept() PiperOrigin-RevId: 274011666 -- 73e8a938fc139e1cc8670d4513a445bacc855539 by Abseil Team : nvcc workaround: explicitly specify the definition of node_handle::Base PiperOrigin-RevId: 274011392 -- ab9cc6d042aca7d48e16c504ab10eab39433f4b2 by Andy Soffer : Internal change PiperOrigin-RevId: 273996318 -- e567c4979ca99c7e71821ec1523b8f5edd2c76ac by Abseil Team : Introduce a type alias to work around an nvcc bug. On the previous code, nvcc gets confused thinking that T has to be a parameter pack, as IsDecomposable accepts one. PiperOrigin-RevId: 273980472 -- 105b6e6339b77a32f4432de05f44cd3f9c436751 by Eric Fiselier : Import of CCTZ from GitHub. PiperOrigin-RevId: 273955589 -- 8feb87ff1d7e721fe094855e67c19539d5e582b7 by Abseil Team : Avoid dual-exporting scheduling_mode.h PiperOrigin-RevId: 273825112 -- fbc37854776d295dae98fb9d06a541f296daab95 by Andy Getzendanner : Fix ABSL_HAVE_ALARM check on mingw Import of https://github.com/abseil/abseil-cpp/pull/341 PiperOrigin-RevId: 273817839 -- 6aedcd63a735b9133e143b043744ba0a25407f6f by Andy Soffer : Remove bit_gen_view.h now that all callers have been migrated to bit_gen_ref.h Tested: TGP - https://test.corp.google.com/ui#id=OCL:273762409:BASE:273743370:1570639020744:3001bcb5 PiperOrigin-RevId: 273810331 -- 6573de24a66ba715c579f7f32b5c48a1d743c7f8 by Abseil Team : Internal change. PiperOrigin-RevId: 273589963 -- 91c8c28b6dca26d98b39e8e06a8ed17c701ff793 by Abseil Team : Update macro name for `ABSL_GUARDED_BY()` in the example section. PiperOrigin-RevId: 273286983 -- 0ff7d1a93d70f8ecd693f8dbb98b7a4a016ca2a4 by Abseil Team : Fix potential integer overflow in the absl time library. In absl::FromTM, the tm.tm_year is added by 1900 regarding that tm.tm_year represents the years since 1900. This change checks integer overflow before doing the arithmetic operation. PiperOrigin-RevId: 273092952 -- b41c2a1310086807be09a833099ae6d4009f037c by Gennadiy Rozental : Correctly Unlock the global mutex in case of concurrent flag initialization. Fixes #386 PiperOrigin-RevId: 272979749 -- c53103e71b2a6063af3c6d4ff68aa2d8f9ae9e06 by Abseil Team : Try to become idle only when there is no wakeup. Immediately after waking up (when futex wait returns), the current thread tries to become idle doing bunch of memory loads and a branch. Problem is that there is a good chance that we woke up due to a wakeup, especially for actively used threads. For such wakeups, calling MaybeBecomeIdle() would be a waste of cycles. Instead, call MaybeBecomeIdle() only when we are sure there is no wakeup. For idle threads the net effect should be the same. For active, threads this will be more efficient. Moreover, since MaybeBecomeIdle() is called before waiting on the futex, the current thread will try to become idle before sleeping. This should result in more accurate idleness and more efficient release of thread resources. PiperOrigin-RevId: 272940381 GitOrigin-RevId: f13697e3d33803f9667d124072da4f6dd8bfbf85 Change-Id: I36de05aec12595183725652dd362dfa58fb095d0 --- absl/BUILD.bazel | 7 +- absl/algorithm/container.h | 18 ++- absl/algorithm/container_test.cc | 6 + absl/base/BUILD.bazel | 16 +-- absl/base/CMakeLists.txt | 9 +- absl/base/attributes.h | 1 - .../inlined_vector_exception_safety_test.cc | 4 +- absl/container/internal/common.h | 4 +- absl/container/internal/raw_hash_set.h | 11 +- absl/copts/configure_copts.bzl | 2 + absl/debugging/CMakeLists.txt | 2 +- absl/debugging/internal/stack_consumption.cc | 12 +- absl/flags/BUILD.bazel | 3 + absl/flags/flag.cc | 4 +- absl/flags/flag.h | 8 +- absl/flags/internal/commandlineflag.cc | 6 + absl/flags/internal/commandlineflag.h | 24 +++- absl/flags/internal/flag.h | 85 +++++++++++++- absl/flags/internal/registry.cc | 106 +++-------------- absl/random/BUILD.bazel | 2 +- absl/random/distribution_format_traits.h | 22 +--- absl/random/distributions.h | 56 ++++++--- absl/random/internal/distributions.h | 30 ----- absl/random/internal/uniform_helper.h | 12 +- absl/strings/BUILD.bazel | 1 + absl/strings/CMakeLists.txt | 1 + absl/strings/charconv_test.cc | 7 ++ absl/strings/internal/charconv_parse.cc | 6 + .../internal/str_format/convert_test.cc | 110 +++++++++++------- absl/strings/numbers_test.cc | 14 +-- absl/strings/string_view.h | 62 +++++----- absl/strings/string_view_test.cc | 23 ++++ absl/synchronization/internal/waiter.cc | 27 ++++- absl/synchronization/internal/waiter.h | 2 +- absl/synchronization/mutex.h | 2 +- absl/time/duration.cc | 5 + absl/time/format.cc | 8 ++ absl/time/internal/cctz/src/time_zone_impl.cc | 11 +- absl/time/time.cc | 14 ++- absl/time/time.h | 9 ++ absl/time/time_test.cc | 24 ++++ 41 files changed, 485 insertions(+), 291 deletions(-) diff --git a/absl/BUILD.bazel b/absl/BUILD.bazel index 853330d447e..5a03acf87ca 100644 --- a/absl/BUILD.bazel +++ b/absl/BUILD.bazel @@ -14,12 +14,15 @@ # limitations under the License. # +load( + ":compiler_config_setting.bzl", + "create_llvm_config", +) + package(default_visibility = ["//visibility:public"]) licenses(["notice"]) # Apache 2.0 -load(":compiler_config_setting.bzl", "create_llvm_config") - create_llvm_config( name = "llvm_compiler", visibility = [":__subpackages__"], diff --git a/absl/algorithm/container.h b/absl/algorithm/container.h index c84de461ac2..adcea8a7999 100644 --- a/absl/algorithm/container.h +++ b/absl/algorithm/container.h @@ -112,6 +112,18 @@ template struct IsUnorderedContainer> : std::true_type {}; +// container_algorithm_internal::c_size. It is meant for internal use only. + +template +auto c_size(C& c) -> decltype(c.size()) { + return c.size(); +} + +template +constexpr std::size_t c_size(T (&)[N]) { + return N; +} + } // namespace container_algorithm_internal // PUBLIC API @@ -365,7 +377,8 @@ c_mismatch(C1& c1, C2& c2, BinaryPredicate&& pred) { template bool c_equal(const C1& c1, const C2& c2) { - return ((c1.size() == c2.size()) && + return ((container_algorithm_internal::c_size(c1) == + container_algorithm_internal::c_size(c2)) && std::equal(container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), container_algorithm_internal::c_begin(c2))); @@ -375,7 +388,8 @@ bool c_equal(const C1& c1, const C2& c2) { // the function's test condition. template bool c_equal(const C1& c1, const C2& c2, BinaryPredicate&& pred) { - return ((c1.size() == c2.size()) && + return ((container_algorithm_internal::c_size(c1) == + container_algorithm_internal::c_size(c2)) && std::equal(container_algorithm_internal::c_begin(c1), container_algorithm_internal::c_end(c1), container_algorithm_internal::c_begin(c2), diff --git a/absl/algorithm/container_test.cc b/absl/algorithm/container_test.cc index 86bf9d3ea32..0a4abe94627 100644 --- a/absl/algorithm/container_test.cc +++ b/absl/algorithm/container_test.cc @@ -163,23 +163,29 @@ TEST_F(NonMutatingTest, MismatchWithPredicate) { TEST_F(NonMutatingTest, Equal) { EXPECT_TRUE(absl::c_equal(vector_, sequence_)); EXPECT_TRUE(absl::c_equal(sequence_, vector_)); + EXPECT_TRUE(absl::c_equal(sequence_, array_)); + EXPECT_TRUE(absl::c_equal(array_, vector_)); // Test that behavior appropriately differs from that of equal(). std::vector vector_plus = {1, 2, 3}; vector_plus.push_back(4); EXPECT_FALSE(absl::c_equal(vector_plus, sequence_)); EXPECT_FALSE(absl::c_equal(sequence_, vector_plus)); + EXPECT_FALSE(absl::c_equal(array_, vector_plus)); } TEST_F(NonMutatingTest, EqualWithPredicate) { EXPECT_TRUE(absl::c_equal(vector_, sequence_, Equals)); EXPECT_TRUE(absl::c_equal(sequence_, vector_, Equals)); + EXPECT_TRUE(absl::c_equal(array_, sequence_, Equals)); + EXPECT_TRUE(absl::c_equal(vector_, array_, Equals)); // Test that behavior appropriately differs from that of equal(). std::vector vector_plus = {1, 2, 3}; vector_plus.push_back(4); EXPECT_FALSE(absl::c_equal(vector_plus, sequence_, Equals)); EXPECT_FALSE(absl::c_equal(sequence_, vector_plus, Equals)); + EXPECT_FALSE(absl::c_equal(vector_plus, array_, Equals)); } TEST_F(NonMutatingTest, IsPermutation) { diff --git a/absl/base/BUILD.bazel b/absl/base/BUILD.bazel index 1e1f0d2de31..d74bac631a6 100644 --- a/absl/base/BUILD.bazel +++ b/absl/base/BUILD.bazel @@ -71,16 +71,16 @@ cc_library( "internal/spinlock_wait.cc", "internal/spinlock_win32.inc", ], - hdrs = [ - "internal/scheduling_mode.h", - "internal/spinlock_wait.h", - ], + hdrs = ["internal/spinlock_wait.h"], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, visibility = [ "//absl/base:__pkg__", ], - deps = [":core_headers"], + deps = [ + ":base_internal", + ":core_headers", + ], ) cc_library( @@ -141,11 +141,11 @@ cc_library( ], deps = [ ":base", + ":base_internal", ":config", ":core_headers", ":dynamic_annotations", ":raw_logging_internal", - ":spinlock_wait", ], ) @@ -369,8 +369,8 @@ cc_library( linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":base", + ":base_internal", ":core_headers", - ":spinlock_wait", "//absl/synchronization", "@com_google_googletest//:gtest", ], @@ -385,8 +385,8 @@ cc_test( linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":base", + ":base_internal", ":core_headers", - ":spinlock_wait", "//absl/synchronization", "@com_google_googletest//:gtest_main", ], diff --git a/absl/base/CMakeLists.txt b/absl/base/CMakeLists.txt index 51705a5a102..79ee5b93ed7 100644 --- a/absl/base/CMakeLists.txt +++ b/absl/base/CMakeLists.txt @@ -56,7 +56,6 @@ absl_cc_library( NAME spinlock_wait HDRS - "internal/scheduling_mode.h" "internal/spinlock_wait.h" SRCS "internal/spinlock_akaros.inc" @@ -67,6 +66,7 @@ absl_cc_library( COPTS ${ABSL_DEFAULT_COPTS} DEPS + absl::base_internal absl::core_headers ) @@ -125,11 +125,11 @@ absl_cc_library( ${ABSL_DEFAULT_COPTS} DEPS absl::base + absl::base_internal absl::config absl::core_headers absl::dynamic_annotations absl::raw_logging_internal - absl::spinlock_wait Threads::Threads ) @@ -141,6 +141,7 @@ absl_cc_library( "internal/identity.h" "internal/inline_variable.h" "internal/invoke.h" + "internal/scheduling_mode.h" COPTS ${ABSL_DEFAULT_COPTS} DEPS @@ -348,8 +349,8 @@ absl_cc_library( ${ABSL_TEST_COPTS} DEPS absl::base + absl::base_internal absl::core_headers - absl::spinlock_wait absl::synchronization gtest TESTONLY @@ -365,8 +366,8 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::base + absl::base_internal absl::core_headers - absl::spinlock_wait absl::synchronization gtest_main ) diff --git a/absl/base/attributes.h b/absl/base/attributes.h index f298297e994..7b7656a859b 100644 --- a/absl/base/attributes.h +++ b/absl/base/attributes.h @@ -599,7 +599,6 @@ // // Note that this attribute is redundant if the variable is declared constexpr. #if ABSL_HAVE_CPP_ATTRIBUTE(clang::require_constant_initialization) -// NOLINTNEXTLINE(whitespace/braces) #define ABSL_CONST_INIT [[clang::require_constant_initialization]] #else #define ABSL_CONST_INIT diff --git a/absl/container/inlined_vector_exception_safety_test.cc b/absl/container/inlined_vector_exception_safety_test.cc index 25994f1685a..937e43a5bf2 100644 --- a/absl/container/inlined_vector_exception_safety_test.cc +++ b/absl/container/inlined_vector_exception_safety_test.cc @@ -16,7 +16,7 @@ #include "absl/base/config.h" -#ifdef ABSL_HAVE_EXCEPTIONS +#if defined(ABSL_HAVE_EXCEPTIONS) #include #include @@ -493,4 +493,4 @@ TYPED_TEST(TwoSizeTest, Swap) { } // namespace -#endif // ABSL_HAVE_EXCEPTIONS +#endif // defined(ABSL_HAVE_EXCEPTIONS) diff --git a/absl/container/internal/common.h b/absl/container/internal/common.h index 591d3ea11fa..4bd5d469746 100644 --- a/absl/container/internal/common.h +++ b/absl/container/internal/common.h @@ -117,7 +117,7 @@ class node_handle_base { template class node_handle : public node_handle_base { - using Base = typename node_handle::node_handle_base; + using Base = node_handle_base; public: using value_type = typename PolicyTraits::value_type; @@ -137,7 +137,7 @@ template class node_handle> : public node_handle_base { - using Base = typename node_handle::node_handle_base; + using Base = node_handle_base; public: using key_type = typename Policy::key_type; diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index 2e6f4dd3e66..42b3c468113 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h @@ -938,8 +938,11 @@ class raw_hash_set { // // flat_hash_map m; // m.insert(std::make_pair("abc", 42)); + // TODO(cheshire): A type alias T2 is introduced as a workaround for the nvcc + // bug. template = 0, - typename std::enable_if::value, int>::type = 0, + class T2 = T, + typename std::enable_if::value, int>::type = 0, T* = nullptr> std::pair insert(T&& value) { return emplace(std::forward(value)); @@ -975,8 +978,10 @@ class raw_hash_set { return emplace(std::move(value)); } - template = 0, - typename std::enable_if::value, int>::type = 0, + // TODO(cheshire): A type alias T2 is introduced as a workaround for the nvcc + // bug. + template = 0, class T2 = T, + typename std::enable_if::value, int>::type = 0, T* = nullptr> iterator insert(const_iterator, T&& value) { return insert(std::forward(value)).first; diff --git a/absl/copts/configure_copts.bzl b/absl/copts/configure_copts.bzl index 2829e4ec6e6..9dd6bd0ae4f 100644 --- a/absl/copts/configure_copts.bzl +++ b/absl/copts/configure_copts.bzl @@ -6,6 +6,8 @@ change Abseil copts, edit absl/copts/copts.py load( "//absl:copts/GENERATED_copts.bzl", + "ABSL_CLANG_CL_FLAGS", + "ABSL_CLANG_CL_TEST_FLAGS", "ABSL_GCC_FLAGS", "ABSL_GCC_TEST_FLAGS", "ABSL_LLVM_FLAGS", diff --git a/absl/debugging/CMakeLists.txt b/absl/debugging/CMakeLists.txt index c409e33e39c..81492c0c0f3 100644 --- a/absl/debugging/CMakeLists.txt +++ b/absl/debugging/CMakeLists.txt @@ -238,7 +238,7 @@ absl_cc_test( SRCS "leak_check_test.cc" COPTS - ${ABSL_DEFAULT_COPTS} + ${ABSL_TEST_COPTS} "$<$:-DABSL_EXPECT_LEAK_SANITIZER>" LINKOPTS "${ABSL_LSAN_LINKOPTS}" diff --git a/absl/debugging/internal/stack_consumption.cc b/absl/debugging/internal/stack_consumption.cc index 4b05f4955ee..d4703264a66 100644 --- a/absl/debugging/internal/stack_consumption.cc +++ b/absl/debugging/internal/stack_consumption.cc @@ -115,10 +115,11 @@ int GetSignalHandlerStackConsumption(void (*signal_handler)(int)) { // Set up the alt-signal-stack (and save the older one). stack_t sigstk; memset(&sigstk, 0, sizeof(sigstk)); - stack_t old_sigstk; sigstk.ss_sp = altstack; sigstk.ss_size = kAlternateStackSize; sigstk.ss_flags = 0; + stack_t old_sigstk; + memset(&old_sigstk, 0, sizeof(old_sigstk)); ABSL_RAW_CHECK(sigaltstack(&sigstk, &old_sigstk) == 0, "sigaltstack() failed"); @@ -152,6 +153,15 @@ int GetSignalHandlerStackConsumption(void (*signal_handler)(int)) { int signal_handler_stack_consumption = GetStackConsumption(altstack); // Now restore the old alt-signal-stack and signal handlers. + if (old_sigstk.ss_sp == nullptr && old_sigstk.ss_size == 0 && + (old_sigstk.ss_flags & SS_DISABLE)) { + // https://git.musl-libc.org/cgit/musl/commit/src/signal/sigaltstack.c?id=7829f42a2c8944555439380498ab8b924d0f2070 + // The original stack has ss_size==0 and ss_flags==SS_DISABLE, but some + // versions of musl have a bug that rejects ss_size==0. Work around this by + // setting ss_size to MINSIGSTKSZ, which should be ignored by the kernel + // when SS_DISABLE is set. + old_sigstk.ss_size = MINSIGSTKSZ; + } ABSL_RAW_CHECK(sigaltstack(&old_sigstk, nullptr) == 0, "sigaltstack() failed"); ABSL_RAW_CHECK(sigaction(SIGUSR1, &old_sa1, nullptr) == 0, diff --git a/absl/flags/BUILD.bazel b/absl/flags/BUILD.bazel index 2bf562f898c..2e0dc3891f5 100644 --- a/absl/flags/BUILD.bazel +++ b/absl/flags/BUILD.bazel @@ -40,6 +40,8 @@ cc_library( deps = [ ":handle", ":registry", + "//absl/memory", + "//absl/strings", "//absl/synchronization", ], ) @@ -184,6 +186,7 @@ cc_library( ":marshalling", "//absl/base", "//absl/base:core_headers", + "//absl/memory", "//absl/strings", ], ) diff --git a/absl/flags/flag.cc b/absl/flags/flag.cc index a02d069e9b0..69038bbf48a 100644 --- a/absl/flags/flag.cc +++ b/absl/flags/flag.cc @@ -49,9 +49,7 @@ namespace flags_internal { ABSL_CONST_INIT static absl::Mutex construction_guard(absl::kConstInit); -void LockGlobalConstructionGuard() { construction_guard.Lock(); } - -void UnlockGlobalConstructionGuard() { construction_guard.Unlock(); } +absl::Mutex* GetGlobalConstructionGuard() { return &construction_guard; } } // namespace flags_internal diff --git a/absl/flags/flag.h b/absl/flags/flag.h index 86ad59dd9f1..4927757b8fb 100644 --- a/absl/flags/flag.h +++ b/absl/flags/flag.h @@ -80,8 +80,7 @@ using Flag = flags_internal::Flag; // if two threads attempt to construct the flag concurrently only one wins. namespace flags_internal { -void LockGlobalConstructionGuard(); -void UnlockGlobalConstructionGuard(); +absl::Mutex* GetGlobalConstructionGuard(); } // namespace flags_internal template @@ -100,7 +99,7 @@ class Flag { flags_internal::Flag* GetImpl() const { if (!inited_.load(std::memory_order_acquire)) { - flags_internal::LockGlobalConstructionGuard(); + absl::MutexLock l(flags_internal::GetGlobalConstructionGuard()); if (inited_.load(std::memory_order_acquire)) { return impl_; @@ -109,8 +108,6 @@ class Flag { impl_ = new flags_internal::Flag(name_, help_gen_, filename_, marshalling_op_, initial_value_gen_); inited_.store(true, std::memory_order_release); - - flags_internal::UnlockGlobalConstructionGuard(); } return impl_; @@ -130,7 +127,6 @@ class Flag { std::string Filename() const { return GetImpl()->Filename(); } std::string DefaultValue() const { return GetImpl()->DefaultValue(); } std::string CurrentValue() const { return GetImpl()->CurrentValue(); } - bool HasValidatorFn() const { return GetImpl()->HasValidatorFn(); } bool InvokeValidator(const void* value) const { return GetImpl()->InvokeValidator(value); } diff --git a/absl/flags/internal/commandlineflag.cc b/absl/flags/internal/commandlineflag.cc index 53e2b84ecc1..99f73611d8f 100644 --- a/absl/flags/internal/commandlineflag.cc +++ b/absl/flags/internal/commandlineflag.cc @@ -149,6 +149,12 @@ std::string CommandLineFlag::CurrentValue() const { return Unparse(marshalling_op_, cur_); } +int64_t CommandLineFlag::MutationCounter() const { + absl::MutexLock l(InitFlagIfNecessary()); + + return counter_; +} + // 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 diff --git a/absl/flags/internal/commandlineflag.h b/absl/flags/internal/commandlineflag.h index 284286b6be1..528d3106bfa 100644 --- a/absl/flags/internal/commandlineflag.h +++ b/absl/flags/internal/commandlineflag.h @@ -17,6 +17,7 @@ #define ABSL_FLAGS_INTERNAL_COMMANDLINEFLAG_H_ #include +#include #include "absl/base/macros.h" #include "absl/flags/marshalling.h" @@ -186,6 +187,16 @@ class HelpText { const char* help_message_; }; +// Handle to FlagState objects. Specific flag state objects will restore state +// of a flag produced this flag state from method CommandLineFlag::SaveState(). +class FlagStateInterface { + public: + virtual ~FlagStateInterface() {} + + // Restores the flag originated this object to the saved state. + virtual void Restore() const = 0; +}; + // Holds all information for a flag. class CommandLineFlag { public: @@ -239,7 +250,6 @@ class CommandLineFlag { virtual void StoreAtomic() {} // Interfaces to operate on validators. - virtual bool HasValidatorFn() const { return false; } virtual bool InvokeValidator(const void* /*value*/) const { return true; } // Invoke the flag validators for old flags. // TODO(rogeeff): implement proper validators for Abseil Flags @@ -264,6 +274,10 @@ class CommandLineFlag { return res; } + // Interface to save flag to some persistent state. Returns current flag state + // or nullptr if flag does not support saving and restoring a state. + virtual std::unique_ptr SaveState() = 0; + // Interfaces to overate on callbacks. virtual void InvokeCallback() {} @@ -285,6 +299,9 @@ class CommandLineFlag { protected: ~CommandLineFlag() = default; + // Thread safe access to mutation counter. + int64_t MutationCounter() const; + const char* const name_; const HelpText help_; const char* const filename_; @@ -323,11 +340,6 @@ class CommandLineFlag { friend bool TryParseLocked(CommandLineFlag* flag, void* dst, absl::string_view value, std::string* err); friend absl::Mutex* InitFlag(CommandLineFlag* flag); - - // This is a short term, until we completely rework persistent state - // storage API. - virtual void* GetValidator() const { return nullptr; } - virtual bool SetValidator(void*) { return false; } }; // Update any copy of the flag value that is stored in an atomic word. diff --git a/absl/flags/internal/flag.h b/absl/flags/internal/flag.h index 16330380f54..2b21c440736 100644 --- a/absl/flags/internal/flag.h +++ b/absl/flags/internal/flag.h @@ -20,12 +20,44 @@ #include "absl/flags/internal/commandlineflag.h" #include "absl/flags/internal/registry.h" +#include "absl/memory/memory.h" +#include "absl/strings/str_cat.h" namespace absl { namespace flags_internal { constexpr int64_t AtomicInit() { return 0xababababababababll; } +template +class Flag; + +template +class FlagState : public flags_internal::FlagStateInterface { + public: + FlagState(Flag* flag, T&& cur, bool modified, bool on_command_line, + int64_t counter) + : flag_(flag), + cur_value_(std::move(cur)), + modified_(modified), + on_command_line_(on_command_line), + counter_(counter) {} + + ~FlagState() override = default; + + private: + friend class Flag; + + // Restores the flag to the saved state. + void Restore() const override; + + // Flag and saved flag data. + Flag* flag_; + T cur_value_; + bool modified_; + bool on_command_line_; + int64_t counter_; +}; + // Signature for the mutation callback used by watched Flags // The callback is noexcept. // TODO(rogeeff): add noexcept after C++17 support is added. @@ -98,15 +130,12 @@ class Flag final : public flags_internal::CommandLineFlag { InvokeCallback(); } - void InvokeCallback() override - ABSL_EXCLUSIVE_LOCKS_REQUIRED(locks_->primary_mu) { - flags_internal::InvokeCallback(&locks_->primary_mu, &locks_->callback_mu, - callback_); - } private: + friend class FlagState; + void Destroy() const override { - // Values are heap allocated Abseil Flags. + // Values are heap allocated for Abseil Flags. if (cur_) Delete(op_, cur_); if (def_) Delete(op_, def_); @@ -121,6 +150,41 @@ class Flag final : public flags_internal::CommandLineFlag { } } + // Interfaces to save and restore flags to/from persistent state. + // Returns current flag state or nullptr if flag does not support + // saving and restoring a state. + std::unique_ptr SaveState() override { + T curr_value = Get(); + + absl::MutexLock l(InitFlagIfNecessary()); + + return absl::make_unique>( + this, std::move(curr_value), modified_, on_command_line_, counter_); + } + + // Restores the flag state to the supplied state object. If there is + // nothing to restore returns false. Otherwise returns true. + bool RestoreState(const flags_internal::FlagState& flag_state) { + if (MutationCounter() == flag_state.counter_) return false; + + Set(flag_state.cur_value_); + + // Race condition here? This should disappear once we move the rest of the + // flag's data into Flag's internals. + + absl::MutexLock l(InitFlagIfNecessary()); + modified_ = flag_state.modified_; + on_command_line_ = flag_state.on_command_line_; + return true; + } + + // Interfaces to overate on callbacks. + void InvokeCallback() override + ABSL_EXCLUSIVE_LOCKS_REQUIRED(locks_->primary_mu) { + flags_internal::InvokeCallback(&locks_->primary_mu, &locks_->callback_mu, + callback_); + } + // Flag's data // For some types, a copy of the current value is kept in an atomically // accessible field. @@ -128,6 +192,15 @@ class Flag final : public flags_internal::CommandLineFlag { FlagCallback callback_; // Mutation callback }; +template +inline void FlagState::Restore() const { + if (flag_->RestoreState(*this)) { + ABSL_INTERNAL_LOG(INFO, + absl::StrCat("Restore saved value of ", flag_->Name(), + " to: ", flag_->CurrentValue())); + } +} + // This class facilitates Flag object registration and tail expression-based // flag definition, for example: // ABSL_FLAG(int, foo, 42, "Foo help").OnUpdate(NotifyFooWatcher); diff --git a/absl/flags/internal/registry.cc b/absl/flags/internal/registry.cc index 4bea313bb1e..6b2564d13a7 100644 --- a/absl/flags/internal/registry.cc +++ b/absl/flags/internal/registry.cc @@ -188,114 +188,34 @@ CommandLineFlag* FlagRegistry::FindRetiredFlagLocked(absl::string_view name) { class FlagSaverImpl { public: - // Constructs an empty FlagSaverImpl object. - FlagSaverImpl() {} - ~FlagSaverImpl() { - // reclaim memory from each of our CommandLineFlags - for (const SavedFlag& src : backup_registry_) { - Delete(src.op, src.current); - Delete(src.op, src.default_value); - } - } + FlagSaverImpl() = default; + FlagSaverImpl(const FlagSaverImpl&) = delete; + void operator=(const FlagSaverImpl&) = delete; // Saves the flag states from the flag registry into this object. // It's an error to call this more than once. - // Must be called when the registry mutex is not held. void SaveFromRegistry() { assert(backup_registry_.empty()); // call only once! - SavedFlag saved; flags_internal::ForEachFlag([&](flags_internal::CommandLineFlag* flag) { - if (flag->IsRetired()) return; - - saved.name = flag->Name(); - saved.op = flag->op_; - saved.marshalling_op = flag->marshalling_op_; - { - absl::MutexLock l(flag->InitFlagIfNecessary()); - saved.validator = flag->GetValidator(); - saved.modified = flag->modified_; - saved.on_command_line = flag->on_command_line_; - saved.current = Clone(saved.op, flag->cur_); - saved.default_value = Clone(saved.op, flag->def_); - saved.counter = flag->counter_; + if (auto flag_state = flag->SaveState()) { + backup_registry_.emplace_back(std::move(flag_state)); } - backup_registry_.push_back(saved); }); } - // Restores the saved flag states into the flag registry. We - // assume no flags were added or deleted from the registry since - // the SaveFromRegistry; if they were, that's trouble! Must be - // called when the registry mutex is not held. + // Restores the saved flag states into the flag registry. void RestoreToRegistry() { - FlagRegistry* const global_registry = FlagRegistry::GlobalRegistry(); - FlagRegistryLock frl(global_registry); - for (const SavedFlag& src : backup_registry_) { - CommandLineFlag* flag = global_registry->FindFlagLocked(src.name); - // If null, flag got deleted from registry. - if (!flag) continue; - - bool restored = false; - { - // This function encapsulate the lock. - flag->SetValidator(src.validator); - - absl::MutexLock l(flag->InitFlagIfNecessary()); - flag->modified_ = src.modified; - flag->on_command_line_ = src.on_command_line; - if (flag->counter_ != src.counter || - ChangedDirectly(flag, src.default_value, flag->def_)) { - restored = true; - Copy(src.op, src.default_value, flag->def_); - } - if (flag->counter_ != src.counter || - ChangedDirectly(flag, src.current, flag->cur_)) { - restored = true; - Copy(src.op, src.current, flag->cur_); - UpdateCopy(flag); - flag->InvokeCallback(); - } - } - - if (restored) { - flag->counter_++; - - // Revalidate the flag because the validator might store state based - // on the flag's value, which just changed due to the restore. - // Failing validation is ignored because it's assumed that the flag - // was valid previously and there's little that can be done about it - // here, anyway. - flag->ValidateInputValue(flag->CurrentValue()); - - ABSL_INTERNAL_LOG( - INFO, absl::StrCat("Restore saved value of ", flag->Name(), ": ", - Unparse(src.marshalling_op, src.current))); - } + for (const auto& flag_state : backup_registry_) { + flag_state->Restore(); } } private: - struct SavedFlag { - absl::string_view name; - FlagOpFn op; - FlagMarshallingOpFn marshalling_op; - int64_t counter; - void* validator; - bool modified; - bool on_command_line; - const void* current; // nullptr after restore - const void* default_value; // nullptr after restore - }; - - std::vector backup_registry_; - - FlagSaverImpl(const FlagSaverImpl&); // no copying! - void operator=(const FlagSaverImpl&); + std::vector> + backup_registry_; }; -FlagSaver::FlagSaver() : impl_(new FlagSaverImpl()) { - impl_->SaveFromRegistry(); -} +FlagSaver::FlagSaver() : impl_(new FlagSaverImpl) { impl_->SaveFromRegistry(); } void FlagSaver::Ignore() { delete impl_; @@ -376,6 +296,10 @@ class RetiredFlagObj final : public flags_internal::CommandLineFlag { delete this; } + + std::unique_ptr SaveState() override { + return nullptr; + } }; } // namespace diff --git a/absl/random/BUILD.bazel b/absl/random/BUILD.bazel index c904618da2a..92111827c15 100644 --- a/absl/random/BUILD.bazel +++ b/absl/random/BUILD.bazel @@ -26,7 +26,7 @@ load( package(default_visibility = ["//visibility:public"]) -licenses(["notice"]) # Apache 2.0 +licenses(["notice"]) cc_library( name = "random", diff --git a/absl/random/distribution_format_traits.h b/absl/random/distribution_format_traits.h index 3298c2cdb64..f9f070589f0 100644 --- a/absl/random/distribution_format_traits.h +++ b/absl/random/distribution_format_traits.h @@ -249,12 +249,12 @@ struct DistributionFormatTraits> { } }; -template +template struct UniformDistributionWrapper; -template -struct DistributionFormatTraits> { - using distribution_t = UniformDistributionWrapper; +template +struct DistributionFormatTraits> { + using distribution_t = UniformDistributionWrapper; using result_t = NumType; static constexpr const char* Name() { return "Uniform"; } @@ -263,19 +263,7 @@ struct DistributionFormatTraits> { return absl::StrCat(Name(), "<", ScalarTypeName(), ">"); } static std::string FormatArgs(const distribution_t& d) { - absl::string_view tag; - if (std::is_same::value) { - tag = "IntervalClosedClosed"; - } else if (std::is_same::value) { - tag = "IntervalClosedOpen"; - } else if (std::is_same::value) { - tag = "IntervalOpenClosed"; - } else if (std::is_same::value) { - tag = "IntervalOpenOpen"; - } else { - tag = "[[unknown tag type]]"; - } - return absl::StrCat(tag, ", ", (d.min)(), ", ", (d.max)()); + return absl::StrCat((d.min)(), ", ", (d.max)()); } static std::string FormatResults(absl::Span results) { return absl::StrJoin(results, ", "); diff --git a/absl/random/distributions.h b/absl/random/distributions.h index 3a4e93abfa2..6ced6061615 100644 --- a/absl/random/distributions.h +++ b/absl/random/distributions.h @@ -124,7 +124,15 @@ Uniform(TagType tag, URBG&& urbg, // NOLINT(runtime/references) R lo, R hi) { using gen_t = absl::decay_t; - return random_internal::UniformImpl(tag, urbg, lo, hi); + using distribution_t = random_internal::UniformDistributionWrapper; + using format_t = random_internal::DistributionFormatTraits; + + auto a = random_internal::uniform_lower_bound(tag, lo, hi); + auto b = random_internal::uniform_upper_bound(tag, lo, hi); + if (a > b) return a; + + return random_internal::DistributionCaller::template Call< + distribution_t, format_t>(&urbg, tag, lo, hi); } // absl::Uniform(bitgen, lo, hi) @@ -135,11 +143,17 @@ template typename absl::enable_if_t::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; + using distribution_t = random_internal::UniformDistributionWrapper; + using format_t = random_internal::DistributionFormatTraits; + + constexpr auto tag = absl::IntervalClosedOpen; + auto a = random_internal::uniform_lower_bound(tag, lo, hi); + auto b = random_internal::uniform_upper_bound(tag, lo, hi); + if (a > b) return a; - return random_internal::UniformImpl(tag, urbg, lo, hi); + return random_internal::DistributionCaller::template Call< + distribution_t, format_t>(&urbg, lo, hi); } // absl::Uniform(tag, bitgen, lo, hi) @@ -156,9 +170,16 @@ Uniform(TagType tag, A lo, B hi) { using gen_t = absl::decay_t; using return_t = typename random_internal::uniform_inferred_return_t; + using distribution_t = random_internal::UniformDistributionWrapper; + using format_t = random_internal::DistributionFormatTraits; - return random_internal::UniformImpl(tag, urbg, lo, - hi); + auto a = random_internal::uniform_lower_bound(tag, lo, hi); + auto b = random_internal::uniform_upper_bound(tag, lo, hi); + if (a > b) return a; + + return random_internal::DistributionCaller::template Call< + distribution_t, format_t>(&urbg, tag, static_cast(lo), + static_cast(hi)); } // absl::Uniform(bitgen, lo, hi) @@ -171,13 +192,19 @@ typename absl::enable_if_t::value, random_internal::uniform_inferred_return_t> 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; using return_t = typename random_internal::uniform_inferred_return_t; + using distribution_t = random_internal::UniformDistributionWrapper; + using format_t = random_internal::DistributionFormatTraits; - return random_internal::UniformImpl(tag, urbg, lo, - hi); + constexpr auto tag = absl::IntervalClosedOpen; + auto a = random_internal::uniform_lower_bound(tag, lo, hi); + auto b = random_internal::uniform_upper_bound(tag, lo, hi); + if (a > b) return a; + + return random_internal::DistributionCaller::template Call< + distribution_t, format_t>(&urbg, static_cast(lo), + static_cast(hi)); } // absl::Uniform(bitgen) @@ -187,13 +214,12 @@ Uniform(URBG&& urbg, // NOLINT(runtime/references) template typename absl::enable_if_t::value, R> // Uniform(URBG&& urbg) { // NOLINT(runtime/references) - constexpr auto tag = absl::IntervalClosedClosed; - constexpr auto lo = std::numeric_limits::lowest(); - constexpr auto hi = (std::numeric_limits::max)(); - using tag_t = decltype(tag); using gen_t = absl::decay_t; + using distribution_t = random_internal::UniformDistributionWrapper; + using format_t = random_internal::DistributionFormatTraits; - return random_internal::UniformImpl(tag, urbg, lo, hi); + return random_internal::DistributionCaller::template Call< + distribution_t, format_t>(&urbg); } // ----------------------------------------------------------------------------- diff --git a/absl/random/internal/distributions.h b/absl/random/internal/distributions.h index 96f8bae3918..c8cec02b7fb 100644 --- a/absl/random/internal/distributions.h +++ b/absl/random/internal/distributions.h @@ -24,36 +24,6 @@ namespace absl { namespace random_internal { -template -struct DistributionFormatTraits; - -// UniformImpl implements the core logic of the Uniform call, which is to -// select the correct distribution type, compute the bounds based on the -// interval tag, and then generate a value. -template -NumType UniformImpl(TagType tag, - URBG& urbg, // NOLINT(runtime/references) - NumType lo, NumType hi) { - static_assert( - std::is_arithmetic::value, - "absl::Uniform() must use an integer or real parameter type."); - - using distribution_t = - UniformDistributionWrapper, NumType>; - using format_t = random_internal::DistributionFormatTraits; - auto a = uniform_lower_bound(tag, lo, hi); - auto b = uniform_upper_bound(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? - if (a > b) return a; - - using gen_t = absl::decay_t; - return DistributionCaller::template Call( - &urbg, tag, lo, hi); -} // In the absence of an explicitly provided return-type, the template // "uniform_inferred_return_t" is used to derive a suitable type, based on diff --git a/absl/random/internal/uniform_helper.h b/absl/random/internal/uniform_helper.h index 2929407e149..f68b1823ef0 100644 --- a/absl/random/internal/uniform_helper.h +++ b/absl/random/internal/uniform_helper.h @@ -154,12 +154,22 @@ using UniformDistribution = absl::uniform_int_distribution, absl::uniform_real_distribution>::type; -template +template struct UniformDistributionWrapper : public UniformDistribution { + template explicit UniformDistributionWrapper(TagType, NumType lo, NumType hi) : UniformDistribution( uniform_lower_bound(TagType{}, lo, hi), uniform_upper_bound(TagType{}, lo, hi)) {} + + explicit UniformDistributionWrapper(NumType lo, NumType hi) + : UniformDistribution( + uniform_lower_bound(IntervalClosedOpenTag(), lo, hi), + uniform_upper_bound(IntervalClosedOpenTag(), lo, hi)) {} + + explicit UniformDistributionWrapper() + : UniformDistribution(std::numeric_limits::lowest(), + (std::numeric_limits::max)()) {} }; } // namespace random_internal diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel index 729bdd761f0..4863ead2992 100644 --- a/absl/strings/BUILD.bazel +++ b/absl/strings/BUILD.bazel @@ -630,6 +630,7 @@ cc_test( visibility = ["//visibility:private"], deps = [ ":str_format_internal", + "//absl/base:raw_logging_internal", "//absl/numeric:int128", "@com_google_googletest//:gtest_main", ], diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt index ccff4441934..3f907957501 100644 --- a/absl/strings/CMakeLists.txt +++ b/absl/strings/CMakeLists.txt @@ -461,6 +461,7 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::str_format_internal + absl::raw_logging_internal absl::int128 gmock_main ) diff --git a/absl/strings/charconv_test.cc b/absl/strings/charconv_test.cc index b58fad269d8..9090e9c89c5 100644 --- a/absl/strings/charconv_test.cc +++ b/absl/strings/charconv_test.cc @@ -511,6 +511,13 @@ TEST(FromChars, Overflow) { EXPECT_EQ(f, std::numeric_limits::max()); } +TEST(FromChars, RegressionTestsFromFuzzer) { + absl::string_view src = "0x21900000p00000000099"; + float f; + auto result = absl::from_chars(src.data(), src.data() + src.size(), f); + EXPECT_EQ(result.ec, std::errc::result_out_of_range); +} + TEST(FromChars, ReturnValuePtr) { // Check that `ptr` points one past the number scanned, even if that number // is not representable. diff --git a/absl/strings/internal/charconv_parse.cc b/absl/strings/internal/charconv_parse.cc index f3c7232403a..fa9a8965fe6 100644 --- a/absl/strings/internal/charconv_parse.cc +++ b/absl/strings/internal/charconv_parse.cc @@ -253,6 +253,12 @@ std::size_t ConsumeDigits(const char* begin, const char* end, int max_digits, assert(max_digits * 4 <= std::numeric_limits::digits); } const char* const original_begin = begin; + + // Skip leading zeros, but only if *out is zero. + // They don't cause an overflow so we don't have to count them for + // `max_digits`. + while (!*out && end != begin && *begin == '0') ++begin; + T accumulator = *out; const char* significant_digits_end = (end - begin > max_digits) ? begin + max_digits : end; diff --git a/absl/strings/internal/str_format/convert_test.cc b/absl/strings/internal/str_format/convert_test.cc index 814ccf4c65e..ab8d5391411 100644 --- a/absl/strings/internal/str_format/convert_test.cc +++ b/absl/strings/internal/str_format/convert_test.cc @@ -5,7 +5,9 @@ #include #include +#include "gmock/gmock.h" #include "gtest/gtest.h" +#include "absl/base/internal/raw_logging.h" #include "absl/strings/internal/str_format/bind.h" namespace absl { @@ -157,12 +159,20 @@ TEST_F(FormatConvertTest, StringPrecision) { EXPECT_EQ("ABC", FormatPack(format2, {FormatArgImpl(p)})); } +// Pointer formatting is implementation defined. This checks that the argument +// can be matched to `ptr`. +MATCHER_P(MatchesPointerString, ptr, "") { + if (ptr == nullptr && arg == "(nil)") { + return true; + } + void* parsed = nullptr; + if (sscanf(arg.c_str(), "%p", &parsed) != 1) { + ABSL_RAW_LOG(FATAL, "Could not parse %s", arg.c_str()); + } + return ptr == parsed; +} + TEST_F(FormatConvertTest, Pointer) { -#ifdef _MSC_VER - // MSVC's printf implementation prints pointers differently. We can't easily - // compare our implementation to theirs. - return; -#endif static int x = 0; const int *xp = &x; char c = 'h'; @@ -173,48 +183,62 @@ TEST_F(FormatConvertTest, Pointer) { using VoidF = void (*)(); VoidF fp = [] {}, fnil = nullptr; volatile char vc; - volatile char* vcp = &vc; - volatile char* vcnil = nullptr; - const FormatArgImpl args[] = { + volatile char *vcp = &vc; + volatile char *vcnil = nullptr; + const FormatArgImpl args_array[] = { FormatArgImpl(xp), FormatArgImpl(cp), FormatArgImpl(inil), FormatArgImpl(cnil), FormatArgImpl(mcp), FormatArgImpl(fp), FormatArgImpl(fnil), FormatArgImpl(vcp), FormatArgImpl(vcnil), }; - struct Expectation { - std::string out; - const char *fmt; - }; - const Expectation kExpect[] = { - {StrPrint("%p", &x), "%p"}, - {StrPrint("%20p", &x), "%20p"}, - {StrPrint("%.1p", &x), "%.1p"}, - {StrPrint("%.20p", &x), "%.20p"}, - {StrPrint("%30.20p", &x), "%30.20p"}, - - {StrPrint("%-p", &x), "%-p"}, - {StrPrint("%-20p", &x), "%-20p"}, - {StrPrint("%-.1p", &x), "%-.1p"}, - {StrPrint("%.20p", &x), "%.20p"}, - {StrPrint("%-30.20p", &x), "%-30.20p"}, - - {StrPrint("%p", cp), "%2$p"}, // const char* - {"(nil)", "%3$p"}, // null const char * - {"(nil)", "%4$p"}, // null const int * - {StrPrint("%p", mcp), "%5$p"}, // nonconst char* - - {StrPrint("%p", fp), "%6$p"}, // function pointer - {StrPrint("%p", vcp), "%8$p"}, // function pointer - -#ifndef __APPLE__ - // Apple's printf differs here (0x0 vs. nil) - {StrPrint("%p", fnil), "%7$p"}, // null function pointer - {StrPrint("%p", vcnil), "%9$p"}, // null function pointer -#endif - }; - for (const Expectation &e : kExpect) { - UntypedFormatSpecImpl format(e.fmt); - EXPECT_EQ(e.out, FormatPack(format, absl::MakeSpan(args))) << e.fmt; - } + auto args = absl::MakeConstSpan(args_array); + + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%p"), args), + MatchesPointerString(&x)); + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%20p"), args), + MatchesPointerString(&x)); + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%.1p"), args), + MatchesPointerString(&x)); + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%.20p"), args), + MatchesPointerString(&x)); + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%30.20p"), args), + MatchesPointerString(&x)); + + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%-p"), args), + MatchesPointerString(&x)); + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%-20p"), args), + MatchesPointerString(&x)); + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%-.1p"), args), + MatchesPointerString(&x)); + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%.20p"), args), + MatchesPointerString(&x)); + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%-30.20p"), args), + MatchesPointerString(&x)); + + // const char* + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%2$p"), args), + MatchesPointerString(cp)); + // null const int* + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%3$p"), args), + MatchesPointerString(nullptr)); + // null const char* + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%4$p"), args), + MatchesPointerString(nullptr)); + // nonconst char* + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%5$p"), args), + MatchesPointerString(mcp)); + + // function pointers + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%6$p"), args), + MatchesPointerString(reinterpret_cast(fp))); + EXPECT_THAT( + FormatPack(UntypedFormatSpecImpl("%8$p"), args), + MatchesPointerString(reinterpret_cast(vcp))); + + // null function pointers + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%7$p"), args), + MatchesPointerString(nullptr)); + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%9$p"), args), + MatchesPointerString(nullptr)); } struct Cardinal { diff --git a/absl/strings/numbers_test.cc b/absl/strings/numbers_test.cc index 77d7e7835b8..4edf8891be3 100644 --- a/absl/strings/numbers_test.cc +++ b/absl/strings/numbers_test.cc @@ -713,11 +713,11 @@ TEST(stringtest, safe_strtou64_base_length_delimited) { } } -// feenableexcept() and fedisableexcept() are missing on macOS, MSVC, -// and WebAssembly. -#if defined(_MSC_VER) || defined(__APPLE__) || defined(__EMSCRIPTEN__) -#define ABSL_MISSING_FEENABLEEXCEPT 1 -#define ABSL_MISSING_FEDISABLEEXCEPT 1 +// feenableexcept() and fedisableexcept() are extensions supported by some libc +// implementations. +#if defined(__GLIBC__) || defined(__BIONIC__) +#define ABSL_HAVE_FEENABLEEXCEPT 1 +#define ABSL_HAVE_FEDISABLEEXCEPT 1 #endif class SimpleDtoaTest : public testing::Test { @@ -725,7 +725,7 @@ class SimpleDtoaTest : public testing::Test { void SetUp() override { // Store the current floating point env & clear away any pending exceptions. feholdexcept(&fp_env_); -#ifndef ABSL_MISSING_FEENABLEEXCEPT +#ifdef ABSL_HAVE_FEENABLEEXCEPT // Turn on floating point exceptions. feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW); #endif @@ -735,7 +735,7 @@ class SimpleDtoaTest : public testing::Test { // Restore the floating point environment to the original state. // In theory fedisableexcept is unnecessary; fesetenv will also do it. // In practice, our toolchains have subtle bugs. -#ifndef ABSL_MISSING_FEDISABLEEXCEPT +#ifdef ABSL_HAVE_FEDISABLEEXCEPT fedisableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW); #endif fesetenv(&fp_env_); diff --git a/absl/strings/string_view.h b/absl/strings/string_view.h index 82344eb246b..3438ccc12a7 100644 --- a/absl/strings/string_view.h +++ b/absl/strings/string_view.h @@ -40,6 +40,13 @@ using std::string_view; #else // ABSL_HAVE_STD_STRING_VIEW +#if ABSL_HAVE_BUILTIN(__builtin_memcmp) || \ + (defined(__GNUC__) && !defined(__clang__)) +#define ABSL_INTERNAL_STRING_VIEW_MEMCMP __builtin_memcmp +#else // ABSL_HAVE_BUILTIN(__builtin_memcmp) +#define ABSL_INTERNAL_STRING_VIEW_MEMCMP memcmp +#endif // ABSL_HAVE_BUILTIN(__builtin_memcmp) + #include #include #include @@ -381,16 +388,13 @@ class string_view { // view. Note that in the case of data equality, a further comparison is made // on the respective sizes of the two `string_view`s to determine which is // smaller, equal, or greater. - int compare(string_view x) const noexcept { - auto min_length = (std::min)(length_, x.length_); - if (min_length > 0) { - int r = memcmp(ptr_, x.ptr_, min_length); - if (r < 0) return -1; - if (r > 0) return 1; - } - if (length_ < x.length_) return -1; - if (length_ > x.length_) return 1; - return 0; + constexpr int compare(string_view x) const noexcept { + return CompareImpl( + length_, x.length_, + length_ == 0 || x.length_ == 0 + ? 0 + : ABSL_INTERNAL_STRING_VIEW_MEMCMP( + ptr_, x.ptr_, length_ < x.length_ ? length_ : x.length_)); } // Overload of `string_view::compare()` for comparing a substring of the @@ -528,6 +532,14 @@ class string_view { #endif } + static constexpr int CompareImpl(size_type length_a, size_type length_b, + int compare_result) { + return compare_result == 0 ? static_cast(length_a > length_b) - + static_cast(length_a < length_b) + : static_cast(compare_result > 0) - + static_cast(compare_result < 0); + } + const char* ptr_; size_type length_; }; @@ -535,33 +547,29 @@ class string_view { // This large function is defined inline so that in a fairly common case where // one of the arguments is a literal, the compiler can elide a lot of the // following comparisons. -inline bool operator==(string_view x, string_view y) noexcept { - auto len = x.size(); - if (len != y.size()) { - return false; - } - - return x.data() == y.data() || len <= 0 || - memcmp(x.data(), y.data(), len) == 0; +constexpr bool operator==(string_view x, string_view y) noexcept { + return x.size() == y.size() && + (x.empty() || + ABSL_INTERNAL_STRING_VIEW_MEMCMP(x.data(), y.data(), x.size()) == 0); } -inline bool operator!=(string_view x, string_view y) noexcept { +constexpr bool operator!=(string_view x, string_view y) noexcept { return !(x == y); } -inline bool operator<(string_view x, string_view y) noexcept { - auto min_size = (std::min)(x.size(), y.size()); - const int r = min_size == 0 ? 0 : memcmp(x.data(), y.data(), min_size); - return (r < 0) || (r == 0 && x.size() < y.size()); +constexpr bool operator<(string_view x, string_view y) noexcept { + return x.compare(y) < 0; } -inline bool operator>(string_view x, string_view y) noexcept { return y < x; } +constexpr bool operator>(string_view x, string_view y) noexcept { + return y < x; +} -inline bool operator<=(string_view x, string_view y) noexcept { +constexpr bool operator<=(string_view x, string_view y) noexcept { return !(y < x); } -inline bool operator>=(string_view x, string_view y) noexcept { +constexpr bool operator>=(string_view x, string_view y) noexcept { return !(x < y); } @@ -570,6 +578,8 @@ std::ostream& operator<<(std::ostream& o, string_view piece); } // namespace absl +#undef ABSL_INTERNAL_STRING_VIEW_MEMCMP + #endif // ABSL_HAVE_STD_STRING_VIEW namespace absl { diff --git a/absl/strings/string_view_test.cc b/absl/strings/string_view_test.cc index eb8b170bbbd..86f2fbcd003 100644 --- a/absl/strings/string_view_test.cc +++ b/absl/strings/string_view_test.cc @@ -974,6 +974,29 @@ TEST(StringViewTest, ConstexprCompiles) { EXPECT_EQ(cstr_strlen.length(), 3); constexpr absl::string_view cstr_strlen2 = "bar"; EXPECT_EQ(cstr_strlen2, "bar"); + +#if ABSL_HAVE_BUILTIN(__builtin_memcmp) || \ + (defined(__GNUC__) && !defined(__clang__)) +#define ABSL_HAVE_CONSTEXPR_STRING_VIEW_COMPARISON 1 +#endif +#ifdef ABSL_HAVE_CONSTEXPR_STRING_VIEW_COMPARISON + constexpr absl::string_view foo = "foo"; + constexpr absl::string_view bar = "bar"; + constexpr bool foo_eq_bar = foo == bar; + constexpr bool foo_ne_bar = foo != bar; + constexpr bool foo_lt_bar = foo < bar; + constexpr bool foo_le_bar = foo <= bar; + constexpr bool foo_gt_bar = foo > bar; + constexpr bool foo_ge_bar = foo >= bar; + constexpr int foo_compare_bar = foo.compare(bar); + EXPECT_FALSE(foo_eq_bar); + EXPECT_TRUE(foo_ne_bar); + EXPECT_FALSE(foo_lt_bar); + EXPECT_FALSE(foo_le_bar); + EXPECT_TRUE(foo_gt_bar); + EXPECT_TRUE(foo_ge_bar); + EXPECT_GT(foo_compare_bar, 0); +#endif #endif #if !defined(__clang__) || 3 < __clang_major__ || \ diff --git a/absl/synchronization/internal/waiter.cc b/absl/synchronization/internal/waiter.cc index 961457809f1..d83d8087f8d 100644 --- a/absl/synchronization/internal/waiter.cc +++ b/absl/synchronization/internal/waiter.cc @@ -129,6 +129,9 @@ void Waiter::Init() { bool Waiter::Wait(KernelTimeout t) { // Loop until we can atomically decrement futex from a positive // value, waiting on a futex while we believe it is zero. + // Note that, since the thread ticker is just reset, we don't need to check + // whether the thread is idle on the very first pass of the loop. + bool first_pass = true; while (true) { int32_t x = futex_.load(std::memory_order_relaxed); if (x != 0) { @@ -140,6 +143,8 @@ bool Waiter::Wait(KernelTimeout t) { return true; // Consumed a wakeup, we are done. } + + if (!first_pass) MaybeBecomeIdle(); const int err = Futex::WaitUntil(&futex_, 0, t); if (err != 0) { if (err == -EINTR || err == -EWOULDBLOCK) { @@ -150,8 +155,7 @@ bool Waiter::Wait(KernelTimeout t) { ABSL_RAW_LOG(FATAL, "Futex operation failed with error %d\n", err); } } - - MaybeBecomeIdle(); + first_pass = false; } } @@ -219,6 +223,9 @@ bool Waiter::Wait(KernelTimeout t) { PthreadMutexHolder h(&mu_); waiter_count_.fetch_add(1, std::memory_order_relaxed); // Loop until we find a wakeup to consume or timeout. + // Note that, since the thread ticker is just reset, we don't need to check + // whether the thread is idle on the very first pass of the loop. + bool first_pass = true; while (true) { int x = wakeup_count_.load(std::memory_order_relaxed); if (x != 0) { @@ -232,6 +239,7 @@ bool Waiter::Wait(KernelTimeout t) { return true; } + if (!first_pass) MaybeBecomeIdle(); // No wakeups available, time to wait. if (!t.has_timeout()) { const int err = pthread_cond_wait(&cv_, &mu_); @@ -248,7 +256,7 @@ bool Waiter::Wait(KernelTimeout t) { ABSL_RAW_LOG(FATAL, "pthread_cond_wait failed: %d", err); } } - MaybeBecomeIdle(); + first_pass = false; } } @@ -288,6 +296,9 @@ bool Waiter::Wait(KernelTimeout t) { } // Loop until we timeout or consume a wakeup. + // Note that, since the thread ticker is just reset, we don't need to check + // whether the thread is idle on the very first pass of the loop. + bool first_pass = true; while (true) { int x = wakeups_.load(std::memory_order_relaxed); if (x != 0) { @@ -300,6 +311,7 @@ bool Waiter::Wait(KernelTimeout t) { return true; } + if (!first_pass) MaybeBecomeIdle(); // Nothing to consume, wait (looping on EINTR). while (true) { if (!t.has_timeout()) { @@ -313,7 +325,7 @@ bool Waiter::Wait(KernelTimeout t) { ABSL_RAW_LOG(FATAL, "sem_timedwait failed: %d", errno); } } - MaybeBecomeIdle(); + first_pass = false; } } @@ -401,6 +413,9 @@ bool Waiter::Wait(KernelTimeout t) { waiter_count_.fetch_add(1, std::memory_order_relaxed); // Loop until we find a wakeup to consume or timeout. + // Note that, since the thread ticker is just reset, we don't need to check + // whether the thread is idle on the very first pass of the loop. + bool first_pass = true; while (true) { int x = wakeup_count_.load(std::memory_order_relaxed); if (x != 0) { @@ -414,6 +429,7 @@ bool Waiter::Wait(KernelTimeout t) { return true; } + if (!first_pass) MaybeBecomeIdle(); // No wakeups available, time to wait. if (!SleepConditionVariableSRW(cv, mu, t.InMillisecondsFromNow(), 0)) { // GetLastError() returns a Win32 DWORD, but we assign to @@ -427,8 +443,7 @@ bool Waiter::Wait(KernelTimeout t) { ABSL_RAW_LOG(FATAL, "SleepConditionVariableSRW failed: %lu", err); } } - - MaybeBecomeIdle(); + first_pass = false; } } diff --git a/absl/synchronization/internal/waiter.h b/absl/synchronization/internal/waiter.h index 0757b91d79d..099935453be 100644 --- a/absl/synchronization/internal/waiter.h +++ b/absl/synchronization/internal/waiter.h @@ -19,7 +19,7 @@ #include "absl/base/config.h" #ifdef _WIN32 -#include +#include #else #include #endif diff --git a/absl/synchronization/mutex.h b/absl/synchronization/mutex.h index d33318d347d..ccd94618d4a 100644 --- a/absl/synchronization/mutex.h +++ b/absl/synchronization/mutex.h @@ -628,7 +628,7 @@ class ABSL_SCOPED_LOCKABLE WriterMutexLock { // Example: // // // assume count_ is not internal reference count -// int count_ GUARDED_BY(mu_); +// int count_ ABSL_GUARDED_BY(mu_); // // mu_.LockWhen(Condition(+[](int* count) { return *count == 0; }, // &count_)); diff --git a/absl/time/duration.cc b/absl/time/duration.cc index a3ac61a9189..9a8768113f3 100644 --- a/absl/time/duration.cc +++ b/absl/time/duration.cc @@ -906,6 +906,11 @@ bool ParseDuration(const std::string& dur_string, Duration* d) { return true; } +bool AbslParseFlag(absl::string_view text, Duration* dst, std::string*) { + return ParseDuration(std::string(text), dst); +} + +std::string AbslUnparseFlag(Duration d) { return FormatDuration(d); } bool ParseFlag(const std::string& text, Duration* dst, std::string* ) { return ParseDuration(text, dst); } diff --git a/absl/time/format.cc b/absl/time/format.cc index d6ca860038d..ebe872cf740 100644 --- a/absl/time/format.cc +++ b/absl/time/format.cc @@ -129,6 +129,14 @@ bool ParseTime(const std::string& format, const std::string& input, } // Functions required to support absl::Time flags. +bool AbslParseFlag(absl::string_view text, absl::Time* t, std::string* error) { + return absl::ParseTime(RFC3339_full, std::string(text), absl::UTCTimeZone(), + t, error); +} + +std::string AbslUnparseFlag(absl::Time t) { + return absl::FormatTime(RFC3339_full, t, absl::UTCTimeZone()); +} bool ParseFlag(const std::string& text, absl::Time* t, std::string* error) { return absl::ParseTime(RFC3339_full, text, absl::UTCTimeZone(), t, error); } diff --git a/absl/time/internal/cctz/src/time_zone_impl.cc b/absl/time/internal/cctz/src/time_zone_impl.cc index a26151d3e91..a241e951d1f 100644 --- a/absl/time/internal/cctz/src/time_zone_impl.cc +++ b/absl/time/internal/cctz/src/time_zone_impl.cc @@ -14,6 +14,7 @@ #include "time_zone_impl.h" +#include #include #include #include @@ -91,8 +92,14 @@ bool time_zone::Impl::LoadTimeZone(const std::string& name, time_zone* tz) { void time_zone::Impl::ClearTimeZoneMapTestOnly() { std::lock_guard lock(TimeZoneMutex()); if (time_zone_map != nullptr) { - // Existing time_zone::Impl* entries are in the wild, so we simply - // leak them. Future requests will result in reloading the data. + // Existing time_zone::Impl* entries are in the wild, so we can't delete + // them. Instead, we move them to a private container, where they are + // logically unreachable but not "leaked". Future requests will result + // in reloading the data. + static auto* cleared = new std::deque; + for (const auto& element : *time_zone_map) { + cleared->push_back(element.second); + } time_zone_map->clear(); } } diff --git a/absl/time/time.cc b/absl/time/time.cc index 6a387bcee61..60382be7b80 100644 --- a/absl/time/time.cc +++ b/absl/time/time.cc @@ -430,9 +430,17 @@ absl::TimeConversion ConvertDateTime(int64_t year, int mon, int day, int hour, } absl::Time FromTM(const struct tm& tm, absl::TimeZone tz) { - const CivilSecond cs(tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, - tm.tm_hour, tm.tm_min, tm.tm_sec); - const auto ti = tz.At(cs); + civil_year_t tm_year = tm.tm_year; + // Avoids years that are too extreme for CivilSecond to normalize. + if (tm_year > 300000000000ll) return InfiniteFuture(); + if (tm_year < -300000000000ll) return InfinitePast(); + int tm_mon = tm.tm_mon; + if (tm_mon == std::numeric_limits::max()) { + tm_mon -= 12; + tm_year += 1; + } + const auto ti = tz.At(CivilSecond(tm_year + 1900, tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec)); return tm.tm_isdst == 0 ? ti.post : ti.pre; } diff --git a/absl/time/time.h b/absl/time/time.h index 9c8f31774ac..0b7312ee983 100644 --- a/absl/time/time.h +++ b/absl/time/time.h @@ -83,6 +83,7 @@ struct timeval; #include #include +#include "absl/base/macros.h" #include "absl/strings/string_view.h" #include "absl/time/civil_time.h" #include "absl/time/internal/cctz/include/cctz/time_zone.h" @@ -545,7 +546,11 @@ bool ParseDuration(const std::string& dur_string, Duration* d); // Support for flag values of type Duration. Duration flags must be specified // in a format that is valid input for absl::ParseDuration(). +bool AbslParseFlag(absl::string_view text, Duration* dst, std::string* error); +std::string AbslUnparseFlag(Duration d); +ABSL_DEPRECATED("Use AbslParseFlag() instead.") bool ParseFlag(const std::string& text, Duration* dst, std::string* error); +ABSL_DEPRECATED("Use AbslUnparseFlag() instead.") std::string UnparseFlag(Duration d); // Time @@ -815,7 +820,11 @@ std::chrono::system_clock::time_point ToChronoTime(Time); // Additionally, if you'd like to specify a time as a count of // seconds/milliseconds/etc from the Unix epoch, use an absl::Duration flag // and add that duration to absl::UnixEpoch() to get an absl::Time. +bool AbslParseFlag(absl::string_view text, Time* t, std::string* error); +std::string AbslUnparseFlag(Time t); +ABSL_DEPRECATED("Use AbslParseFlag() instead.") bool ParseFlag(const std::string& text, Time* t, std::string* error); +ABSL_DEPRECATED("Use AbslUnparseFlag() instead.") std::string UnparseFlag(Time t); // TimeZone diff --git a/absl/time/time_test.cc b/absl/time/time_test.cc index 37af39d9983..9c4709e6b46 100644 --- a/absl/time/time_test.cc +++ b/absl/time/time_test.cc @@ -795,6 +795,30 @@ TEST(Time, FromTM) { tm.tm_isdst = 1; t = FromTM(tm, nyc); EXPECT_EQ("2014-03-09T03:30:42-04:00", absl::FormatTime(t, nyc)); // DST + + // Adjusts tm to refer to a time with a year larger than 2147483647. + tm.tm_year = 2147483647 - 1900 + 1; + tm.tm_mon = 6 - 1; + tm.tm_mday = 28; + tm.tm_hour = 1; + tm.tm_min = 2; + tm.tm_sec = 3; + tm.tm_isdst = -1; + t = FromTM(tm, absl::UTCTimeZone()); + EXPECT_EQ("2147483648-06-28T01:02:03+00:00", + absl::FormatTime(t, absl::UTCTimeZone())); + + // Adjusts tm to refer to a time with a very large month. + tm.tm_year = 2019 - 1900; + tm.tm_mon = 2147483647; + tm.tm_mday = 28; + tm.tm_hour = 1; + tm.tm_min = 2; + tm.tm_sec = 3; + tm.tm_isdst = -1; + t = FromTM(tm, absl::UTCTimeZone()); + EXPECT_EQ("178958989-08-28T01:02:03+00:00", + absl::FormatTime(t, absl::UTCTimeZone())); } TEST(Time, TMRoundTrip) { From a15364ce4d88534ae2295127e5d8e32aefb6b446 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Wed, 16 Oct 2019 15:51:11 -0700 Subject: [PATCH 0005/3164] Export of internal Abseil changes -- d35c72d705155dcd89a92835103540f14c643d10 by Gennadiy Rozental : helpxml changed to report types of flags with built-in value type. PiperOrigin-RevId: 275131440 -- f3478792943d7dd40a6ef6083a8e5d374f43a65e by Abseil Team : Add space padding tests for hex conversions. PiperOrigin-RevId: 275120155 -- 7c2e4725403e173660f33f94af686a75d3722936 by Andy Soffer : Fix https://github.com/abseil/abseil-cpp/issues/379 by renaming `roundup` to `round_up` PiperOrigin-RevId: 275106110 -- 84cb30d6ee509961ac4359cfdda1360973b9527d by Laramie Leavitt : Move random_internal::wide_multiply into a file by the same name. PiperOrigin-RevId: 275059359 -- 06d691a8c187b5d899e7863784b23bdcfd580cb2 by Abseil Team : Add missing "return" keyword. PiperOrigin-RevId: 275036408 GitOrigin-RevId: d35c72d705155dcd89a92835103540f14c643d10 Change-Id: Id837b4de6c9cfe18f0a088363754bfe389df985b --- absl/base/internal/low_level_alloc.cc | 16 +-- absl/flags/flag.h | 2 +- absl/flags/internal/usage.cc | 26 ++++- absl/random/BUILD.bazel | 1 + absl/random/CMakeLists.txt | 34 ++++++ absl/random/internal/BUILD.bazel | 27 +++++ absl/random/internal/distribution_impl.h | 66 ----------- .../random/internal/distribution_impl_test.cc | 42 ------- absl/random/internal/wide_multiply.h | 109 ++++++++++++++++++ absl/random/internal/wide_multiply_test.cc | 66 +++++++++++ absl/random/uniform_int_distribution.h | 1 + absl/strings/numbers_test.cc | 3 + 12 files changed, 275 insertions(+), 118 deletions(-) create mode 100644 absl/random/internal/wide_multiply.h create mode 100644 absl/random/internal/wide_multiply_test.cc diff --git a/absl/base/internal/low_level_alloc.cc b/absl/base/internal/low_level_alloc.cc index 64d7aa80b13..f7314ab1899 100644 --- a/absl/base/internal/low_level_alloc.cc +++ b/absl/base/internal/low_level_alloc.cc @@ -211,7 +211,7 @@ struct LowLevelAlloc::Arena { // Result of sysconf(_SC_PAGESIZE) const size_t pagesize; // Lowest power of two >= max(16, sizeof(AllocList)) - const size_t roundup; + const size_t round_up; // Smallest allocation block size const size_t min_size; // PRNG state @@ -336,11 +336,11 @@ size_t GetPageSize() { size_t RoundedUpBlockSize() { // Round up block sizes to a power of two close to the header size. - size_t roundup = 16; - while (roundup < sizeof(AllocList::Header)) { - roundup += roundup; + size_t round_up = 16; + while (round_up < sizeof(AllocList::Header)) { + round_up += round_up; } - return roundup; + return round_up; } } // namespace @@ -350,8 +350,8 @@ LowLevelAlloc::Arena::Arena(uint32_t flags_value) allocation_count(0), flags(flags_value), pagesize(GetPageSize()), - roundup(RoundedUpBlockSize()), - min_size(2 * roundup), + round_up(RoundedUpBlockSize()), + min_size(2 * round_up), random(0) { freelist.header.size = 0; freelist.header.magic = @@ -528,7 +528,7 @@ static void *DoAllocWithArena(size_t request, LowLevelAlloc::Arena *arena) { ArenaLock section(arena); // round up with header size_t req_rnd = RoundUp(CheckedAdd(request, sizeof (s->header)), - arena->roundup); + arena->round_up); for (;;) { // loop until we find a suitable region // find the minimum levels that a block of this size must have int i = LLA_SkiplistLevels(req_rnd, arena->min_size, nullptr) - 1; diff --git a/absl/flags/flag.h b/absl/flags/flag.h index 4927757b8fb..85900ef81b3 100644 --- a/absl/flags/flag.h +++ b/absl/flags/flag.h @@ -121,7 +121,7 @@ class Flag { bool IsModified() const { return GetImpl()->IsModified(); } void SetModified(bool is_modified) { GetImpl()->SetModified(is_modified); } bool IsSpecifiedOnCommandLine() const { - GetImpl()->IsSpecifiedOnCommandLine(); + return GetImpl()->IsSpecifiedOnCommandLine(); } absl::string_view Typename() const { return GetImpl()->Typename(); } std::string Filename() const { return GetImpl()->Filename(); } diff --git a/absl/flags/internal/usage.cc b/absl/flags/internal/usage.cc index 03048514ae0..181c35c6190 100644 --- a/absl/flags/internal/usage.cc +++ b/absl/flags/internal/usage.cc @@ -47,6 +47,27 @@ namespace absl { namespace flags_internal { namespace { +absl::string_view TypenameForHelp(const flags_internal::CommandLineFlag& flag) { + // Only report names of v1 built-in types +#define HANDLE_V1_BUILTIN_TYPE(t) \ + if (flag.IsOfType()) { \ + return #t; \ + } + + HANDLE_V1_BUILTIN_TYPE(bool); + HANDLE_V1_BUILTIN_TYPE(int32_t); + HANDLE_V1_BUILTIN_TYPE(int64_t); + HANDLE_V1_BUILTIN_TYPE(uint64_t); + HANDLE_V1_BUILTIN_TYPE(double); +#undef HANDLE_V1_BUILTIN_TYPE + + if (flag.IsOfType()) { + return "string"; + } + + return ""; +} + // This class is used to emit an XML element with `tag` and `text`. // It adds opening and closing tags and escapes special characters in the text. // For example: @@ -186,7 +207,7 @@ void FlagHelpHumanReadable(const flags_internal::CommandLineFlag& flag, // Flag data type (for V1 flags only). if (!flag.IsAbseilFlag() && !flag.IsRetired()) { - printer.Write(absl::StrCat("type: ", flag.Typename(), ";")); + printer.Write(absl::StrCat("type: ", TypenameForHelp(flag), ";")); } // The listed default value will be the actual default from the flag @@ -223,6 +244,9 @@ void FlagsHelpImpl(std::ostream& out, flags_internal::FlagKindFilter filter_cb, } else { // XML schema is not a part of our public API for now. out << "\n" + << "\n" + << "\n" // The document. << "\n" // The program name and usage. diff --git a/absl/random/BUILD.bazel b/absl/random/BUILD.bazel index 92111827c15..828f1348bac 100644 --- a/absl/random/BUILD.bazel +++ b/absl/random/BUILD.bazel @@ -76,6 +76,7 @@ cc_library( "//absl/random/internal:iostream_state_saver", "//absl/random/internal:traits", "//absl/random/internal:uniform_helper", + "//absl/random/internal:wide_multiply", "//absl/strings", "//absl/types:span", ], diff --git a/absl/random/CMakeLists.txt b/absl/random/CMakeLists.txt index 151cefa4bf1..19dd2cabf57 100644 --- a/absl/random/CMakeLists.txt +++ b/absl/random/CMakeLists.txt @@ -65,6 +65,7 @@ absl_cc_library( absl::random_internal_iostream_state_saver absl::random_internal_traits absl::random_internal_uniform_helper + absl::random_internal_wide_multiply absl::strings absl::span absl::type_traits @@ -557,6 +558,22 @@ absl_cc_library( absl::random_internal_traits ) +# Internal-only target, do not depend on directly. +absl_cc_library( + NAME + random_internal_wide_multiply + HDRS + "internal/wide_multiply.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::bits + absl::config + absl::int128 +) + # Internal-only target, do not depend on directly. absl_cc_library( NAME @@ -1033,3 +1050,20 @@ absl_cc_test( absl::random_internal_iostream_state_saver gtest_main ) + +# Internal-only target, do not depend on directly. +absl_cc_test( + NAME + random_internal_wide_multiply_test + SRCS + internal/wide_multiply_test.cc + COPTS + ${ABSL_TEST_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::random_internal_wide_multiply + absl::bits + absl::int128 + gtest_main +) diff --git a/absl/random/internal/BUILD.bazel b/absl/random/internal/BUILD.bazel index cd50982d8b3..ec58cecd43e 100644 --- a/absl/random/internal/BUILD.bazel +++ b/absl/random/internal/BUILD.bazel @@ -200,6 +200,19 @@ cc_library( deps = ["//absl/base:bits"], ) +cc_library( + name = "wide_multiply", + hdrs = ["wide_multiply.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":traits", + "//absl/base:bits", + "//absl/base:config", + "//absl/numeric:int128", + ], +) + cc_library( name = "nonsecure_base", hdrs = ["nonsecure_base.h"], @@ -598,6 +611,20 @@ cc_test( ], ) +cc_test( + name = "wide_multiply_test", + size = "small", + srcs = ["wide_multiply_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":wide_multiply", + "//absl/base:bits", + "//absl/numeric:int128", + "@com_google_googletest//:gtest_main", + ], +) + cc_library( name = "nanobenchmark", srcs = ["nanobenchmark.cc"], diff --git a/absl/random/internal/distribution_impl.h b/absl/random/internal/distribution_impl.h index 9b6ffb0fb50..49b3e1a6e33 100644 --- a/absl/random/internal/distribution_impl.h +++ b/absl/random/internal/distribution_impl.h @@ -188,72 +188,6 @@ struct RandU64ToReal { } }; -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(a); - const uint64_t a32 = a >> 32; - const uint64_t b00 = static_cast(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(((c00 >> 32) + static_cast(c32a) + - static_cast(c32b)) >> - 32); - - return absl::MakeUint128(c64 + (c32a >> 32) + (c32b >> 32) + carry, - c00 + (c32a << 32) + (c32b << 32)); -#endif -} - -// wide_multiply multiplies two N-bit values to a 2N-bit result. -template -struct wide_multiply { - static constexpr size_t kN = std::numeric_limits::digits; - using input_type = UIntType; - using result_type = typename random_internal::unsigned_bits::type; - - static result_type multiply(input_type a, input_type b) { - return static_cast(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::value, - "Class-template wide_multiply<> argument must be unsigned."); -}; - -#ifndef ABSL_HAVE_INTRINSIC_INT128 -template <> -struct wide_multiply { - 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 } // namespace absl diff --git a/absl/random/internal/distribution_impl_test.cc b/absl/random/internal/distribution_impl_test.cc index 09e7a318ccc..fcc169046d7 100644 --- a/absl/random/internal/distribution_impl_test.cc +++ b/absl/random/internal/distribution_impl_test.cc @@ -461,46 +461,4 @@ TEST(DistributionImplTest, ExhaustiveFloat) { } } -TEST(DistributionImplTest, MultiplyU64ToU128Test) { - using absl::random_internal::MultiplyU64ToU128; - constexpr uint64_t k1 = 1; - constexpr uint64_t kMax = ~static_cast(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/wide_multiply.h b/absl/random/internal/wide_multiply.h new file mode 100644 index 00000000000..ebbfa1f2c10 --- /dev/null +++ b/absl/random/internal/wide_multiply.h @@ -0,0 +1,109 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_RANDOM_INTERNAL_WIDE_MULTIPLY_H_ +#define ABSL_RANDOM_INTERNAL_WIDE_MULTIPLY_H_ + +#include +#include +#include + +#if (defined(_WIN32) || defined(_WIN64)) && defined(_M_IA64) +#include // NOLINT(build/include_order) +#pragma intrinsic(_umul128) +#define ABSL_INTERNAL_USE_UMUL128 1 +#endif + +#include "absl/base/config.h" +#include "absl/base/internal/bits.h" +#include "absl/numeric/int128.h" +#include "absl/random/internal/traits.h" + +namespace absl { +namespace random_internal { + +// Helper object to multiply two 64-bit values to a 128-bit value. +// MultiplyU64ToU128 multiplies two 64-bit values to a 128-bit value. +// If an intrinsic is available, it is used, otherwise use native 32-bit +// multiplies to construct the result. +inline uint128 MultiplyU64ToU128(uint64_t a, uint64_t b) { +#if defined(ABSL_HAVE_INTRINSIC_INT128) + return uint128(static_cast<__uint128_t>(a) * b); +#elif defined(ABSL_INTERNAL_USE_UMUL128) + // uint64_t * uint64_t => uint128 multiply using imul intrinsic on MSVC. + uint64_t high = 0; + const uint64_t low = _umul128(a, b, &high); + return absl::MakeUint128(high, low); +#else + // uint128(a) * uint128(b) in emulated mode computes a full 128-bit x 128-bit + // multiply. However there are many cases where that is not necessary, and it + // is only necessary to support a 64-bit x 64-bit = 128-bit multiply. This is + // for those cases. + const uint64_t a00 = static_cast(a); + const uint64_t a32 = a >> 32; + const uint64_t b00 = static_cast(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(((c00 >> 32) + static_cast(c32a) + + static_cast(c32b)) >> + 32); + + return absl::MakeUint128(c64 + (c32a >> 32) + (c32b >> 32) + carry, + c00 + (c32a << 32) + (c32b << 32)); +#endif +} + +// wide_multiply multiplies two N-bit values to a 2N-bit result. +template +struct wide_multiply { + static constexpr size_t kN = std::numeric_limits::digits; + using input_type = UIntType; + using result_type = typename random_internal::unsigned_bits::type; + + static result_type multiply(input_type a, input_type b) { + return static_cast(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::value, + "Class-template wide_multiply<> argument must be unsigned."); +}; + +#ifndef ABSL_HAVE_INTRINSIC_INT128 +template <> +struct wide_multiply { + 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 +} // namespace absl + +#endif // ABSL_RANDOM_INTERNAL_WIDE_MULTIPLY_H_ diff --git a/absl/random/internal/wide_multiply_test.cc b/absl/random/internal/wide_multiply_test.cc new file mode 100644 index 00000000000..922603f2262 --- /dev/null +++ b/absl/random/internal/wide_multiply_test.cc @@ -0,0 +1,66 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/random/internal/wide_multiply.h" + +#include "gtest/gtest.h" +#include "absl/base/internal/bits.h" +#include "absl/numeric/int128.h" + +using absl::random_internal::MultiplyU64ToU128; + +namespace { + +TEST(WideMultiplyTest, MultiplyU64ToU128Test) { + constexpr uint64_t k1 = 1; + constexpr uint64_t kMax = ~static_cast(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/uniform_int_distribution.h b/absl/random/uniform_int_distribution.h index 4970486a561..02aa376555e 100644 --- a/absl/random/uniform_int_distribution.h +++ b/absl/random/uniform_int_distribution.h @@ -38,6 +38,7 @@ #include "absl/random/internal/fast_uniform_bits.h" #include "absl/random/internal/iostream_state_saver.h" #include "absl/random/internal/traits.h" +#include "absl/random/internal/wide_multiply.h" namespace absl { diff --git a/absl/strings/numbers_test.cc b/absl/strings/numbers_test.cc index 4edf8891be3..d964e562b98 100644 --- a/absl/strings/numbers_test.cc +++ b/absl/strings/numbers_test.cc @@ -204,6 +204,9 @@ void CheckHex64(uint64_t v) { std::string actual = absl::StrCat(absl::Hex(v, absl::kZeroPad16)); snprintf(expected, sizeof(expected), "%016" PRIx64, static_cast(v)); EXPECT_EQ(expected, actual) << " Input " << v; + actual = absl::StrCat(absl::Hex(v, absl::kSpacePad16)); + snprintf(expected, sizeof(expected), "%16" PRIx64, static_cast(v)); + EXPECT_EQ(expected, actual) << " Input " << v; } TEST(Numbers, TestFastPrints) { From e4c8d0eb8ef4acb5d7a4252b3b87feb391ef7e41 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Fri, 18 Oct 2019 09:06:29 -0700 Subject: [PATCH 0006/3164] Export of internal Abseil changes -- a9ac6567c0933d786d68c10011e3f3ff9deedf89 by Greg Falcon : Add absl::FunctionRef, a type analogous to the proposed C++23 std::function_ref. Like std::function, FunctionRef can be used to type-erase any callable (invokable) object. However, FunctionRef works by reference: it does not store a copy of the type-erased object. If the wrapped object is destroyed before the FunctionRef, the reference becomes dangling. FunctionRef relates to std::function in much the same way that string_view relates to std::string. Because of these limitations, FunctionRef is best used only as a function argument type, and only where the function will be invoked immediately (rather than saved for later use). When `const std::function<...>&` is used in this way, `absl::FunctionRef<...>` is a better-performing replacement. PiperOrigin-RevId: 275484044 -- 1f7c4df3760f8b93e5a5baf40b070eca1d3f4c98 by Abseil Team : Add FastHexToBufferZeroPad16() function for blazingly fast hex encoding of uint64_t. PiperOrigin-RevId: 275420901 -- 08d48ac004eba57cf2f1ada827181a2995f74807 by Abseil Team : Avoid applying the workaround for MSVC's static initialization problems when using clang-cl. PiperOrigin-RevId: 275366326 -- 40be82bd2b34670b5458c0a72a0475086153c2d6 by Abseil Team : Added comments to SimpleAtof()/SimpleAtod() that clarify that they always use the "C" locale, unlike the standard functions strtod() and strtof() referenced now in the comments. PiperOrigin-RevId: 275355815 -- 086779dacb3f6f2b3ab59947e94e79046bdb1fe1 by Jorg Brown : Move the hex conversion table used by escaping.cc into numbers.h so that other parts of Abseil can more efficiently access it. PiperOrigin-RevId: 275331251 -- 3c4ed1b04e55d96a40cbe70fb70929ffbb0c0432 by Abseil Team : Avoid applying the workaround for MSVC's static initialization problems when using clang-cl. PiperOrigin-RevId: 275323858 -- 56ceb58ab688c3761978308609b09a1ac2739c9a by Derek Mauro : Add script for testing on Alpine Linux (for musl test coverage) PiperOrigin-RevId: 275321244 GitOrigin-RevId: a9ac6567c0933d786d68c10011e3f3ff9deedf89 Change-Id: I39799fa03768ddb44f3166200c860e1da4461807 --- absl/functional/BUILD.bazel | 51 +++++ absl/functional/function_ref.h | 137 ++++++++++++ absl/functional/function_ref_benchmark.cc | 140 ++++++++++++ absl/functional/function_ref_test.cc | 255 ++++++++++++++++++++++ absl/functional/internal/function_ref.h | 104 +++++++++ absl/strings/BUILD.bazel | 5 +- absl/strings/CMakeLists.txt | 2 + absl/strings/escaping.cc | 33 +-- absl/strings/numbers.cc | 29 ++- absl/strings/numbers.h | 45 +++- absl/strings/numbers_benchmark.cc | 23 ++ absl/strings/numbers_test.cc | 30 ++- absl/strings/str_cat.cc | 5 +- absl/strings/substitute.cc | 5 +- ci/linux_gcc_alpine_cmake.sh | 63 ++++++ 15 files changed, 887 insertions(+), 40 deletions(-) create mode 100644 absl/functional/BUILD.bazel create mode 100644 absl/functional/function_ref.h create mode 100644 absl/functional/function_ref_benchmark.cc create mode 100644 absl/functional/function_ref_test.cc create mode 100644 absl/functional/internal/function_ref.h create mode 100755 ci/linux_gcc_alpine_cmake.sh diff --git a/absl/functional/BUILD.bazel b/absl/functional/BUILD.bazel new file mode 100644 index 00000000000..e861b8a5601 --- /dev/null +++ b/absl/functional/BUILD.bazel @@ -0,0 +1,51 @@ +load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") +load( + "//absl:copts/configure_copts.bzl", + "ABSL_DEFAULT_COPTS", + "ABSL_DEFAULT_LINKOPTS", + "ABSL_TEST_COPTS", +) + +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) # Apache 2.0 + +cc_library( + name = "function_ref", + srcs = ["internal/function_ref.h"], + hdrs = ["function_ref.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + "//absl/base:base_internal", + "//absl/meta:type_traits", + ], +) + +cc_test( + name = "function_ref_test", + size = "small", + srcs = ["function_ref_test.cc"], + copts = ABSL_TEST_COPTS, + deps = [ + ":function_ref", + "//absl/container:test_instance_tracker", + "//absl/memory", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "function_ref_benchmark", + srcs = [ + "function_ref_benchmark.cc", + ], + copts = ABSL_TEST_COPTS, + tags = ["benchmark"], + visibility = ["//visibility:private"], + deps = [ + ":function_ref", + "//absl/base:core_headers", + "@com_github_google_benchmark//:benchmark_main", + ], +) diff --git a/absl/functional/function_ref.h b/absl/functional/function_ref.h new file mode 100644 index 00000000000..42d9f16fca6 --- /dev/null +++ b/absl/functional/function_ref.h @@ -0,0 +1,137 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: function_ref.h +// ----------------------------------------------------------------------------- +// +// This header file defines the `absl::FunctionRef` type for holding a +// non-owning reference to an object of any invocable type. This function +// reference is typically most useful as a type-erased argument type for +// accepting function types that neither take ownership nor copy the type; using +// the reference type in this case avoids a copy and an allocation. Best +// practices of other non-owning reference-like objects (such as +// `absl::string_view`) apply here. +// +// An `absl::FunctionRef` is similar in usage to a `std::function` but has the +// following differences: +// +// * It doesn't own the underlying object. +// * It doesn't have a null or empty state. +// * It never performs deep copies or allocations. +// * It's much faster and cheaper to construct. +// * It's trivially copyable and destructable. +// +// Generally, `absl::FunctionRef` should not be used as a return value, data +// member, or to initialize a `std::function`. Such usages will often lead to +// problematic lifetime issues. Once you convert something to an +// `absl::FunctionRef` you cannot make a deep copy later. +// +// This class is suitable for use wherever a "const std::function<>&" +// would be used without making a copy. ForEach functions and other versions of +// the visitor pattern are a good example of when this class should be used. +// +// This class is trivial to copy and should be passed by value. +#ifndef ABSL_FUNCTIONAL_FUNCTION_REF_H_ +#define ABSL_FUNCTIONAL_FUNCTION_REF_H_ + +#include +#include +#include + +#include "absl/functional/internal/function_ref.h" +#include "absl/meta/type_traits.h" + +namespace absl { + +// FunctionRef +// +// Dummy class declaration to allow the partial specialization based on function +// types below. +template +class FunctionRef; + +// FunctionRef +// +// An `absl::FunctionRef` is a lightweight wrapper to any invokable object with +// a compatible signature. Generally, an `absl::FunctionRef` should only be used +// as an argument type and should be preferred as an argument over a const +// reference to a `std::function`. +// +// Example: +// +// // The following function takes a function callback by const reference +// bool Visitor(const std::function& callback); +// +// // Assuming that the function is not stored or otherwise copied, it can be +// // replaced by an `absl::FunctionRef`: +// bool Visitor(absl::FunctionRef +// callback); +// +// Note: the assignment operator within an `absl::FunctionRef` is intentionally +// deleted to prevent misuse; because the `absl::FunctionRef` does not own the +// underlying type, assignment likely indicates misuse. +template +class FunctionRef { + private: + // Used to disable constructors for objects that are not compatible with the + // signature of this FunctionRef. + template > + using EnableIfCompatible = + typename std::enable_if::value || + std::is_convertible::value>::type; + + public: + // Constructs a FunctionRef from any invokable type. + template > + FunctionRef(const F& f) // NOLINT(runtime/explicit) + : invoker_(&absl::functional_internal::InvokeObject) { + absl::functional_internal::AssertNonNull(f); + ptr_.obj = &f; + } + + // Overload for function pointers. This eliminates a level of indirection that + // would happen if the above overload was used (it lets us store the pointer + // instead of a pointer to a pointer). + // + // This overload is also used for references to functions, since references to + // functions can decay to function pointers implicitly. + template < + typename F, typename = EnableIfCompatible, + absl::functional_internal::EnableIf::value> = 0> + FunctionRef(F* f) // NOLINT(runtime/explicit) + : invoker_(&absl::functional_internal::InvokeFunction) { + assert(f != nullptr); + ptr_.fun = reinterpret_cast(f); + } + + // To help prevent subtle lifetime bugs, FunctionRef is not assignable. + // Typically, it should only be used as an argument type. + FunctionRef& operator=(const FunctionRef& rhs) = delete; + + // Call the underlying object. + R operator()(Args... args) const { + return invoker_(ptr_, std::forward(args)...); + } + + private: + absl::functional_internal::VoidPtr ptr_; + absl::functional_internal::Invoker invoker_; +}; + +} // namespace absl + +#endif // ABSL_FUNCTIONAL_FUNCTION_REF_H_ diff --git a/absl/functional/function_ref_benchmark.cc b/absl/functional/function_ref_benchmark.cc new file mode 100644 index 00000000000..f6dfcabf110 --- /dev/null +++ b/absl/functional/function_ref_benchmark.cc @@ -0,0 +1,140 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/functional/function_ref.h" + +#include + +#include "benchmark/benchmark.h" +#include "absl/base/attributes.h" + +namespace absl { +namespace { + +int dummy = 0; + +void FreeFunction() { benchmark::DoNotOptimize(dummy); } + +struct TrivialFunctor { + void operator()() const { benchmark::DoNotOptimize(dummy); } +}; + +struct LargeFunctor { + void operator()() const { benchmark::DoNotOptimize(this); } + std::string a, b, c; +}; + +template +void ABSL_ATTRIBUTE_NOINLINE CallFunction(Function f, Args&&... args) { + f(std::forward(args)...); +} + +template +void ConstructAndCallFunctionBenchmark(benchmark::State& state, + const Callable& c, Args&&... args) { + for (auto _ : state) { + CallFunction(c, std::forward(args)...); + } +} + +void BM_TrivialStdFunction(benchmark::State& state) { + ConstructAndCallFunctionBenchmark>(state, + TrivialFunctor{}); +} +BENCHMARK(BM_TrivialStdFunction); + +void BM_TrivialFunctionRef(benchmark::State& state) { + ConstructAndCallFunctionBenchmark>(state, + TrivialFunctor{}); +} +BENCHMARK(BM_TrivialFunctionRef); + +void BM_LargeStdFunction(benchmark::State& state) { + ConstructAndCallFunctionBenchmark>(state, + LargeFunctor{}); +} +BENCHMARK(BM_LargeStdFunction); + +void BM_LargeFunctionRef(benchmark::State& state) { + ConstructAndCallFunctionBenchmark>(state, LargeFunctor{}); +} +BENCHMARK(BM_LargeFunctionRef); + +void BM_FunPtrStdFunction(benchmark::State& state) { + ConstructAndCallFunctionBenchmark>(state, FreeFunction); +} +BENCHMARK(BM_FunPtrStdFunction); + +void BM_FunPtrFunctionRef(benchmark::State& state) { + ConstructAndCallFunctionBenchmark>(state, FreeFunction); +} +BENCHMARK(BM_FunPtrFunctionRef); + +// Doesn't include construction or copy overhead in the loop. +template +void CallFunctionBenchmark(benchmark::State& state, const Callable& c, + Args... args) { + Function f = c; + for (auto _ : state) { + benchmark::DoNotOptimize(&f); + f(args...); + } +} + +struct FunctorWithTrivialArgs { + void operator()(int a, int b, int c) const { + benchmark::DoNotOptimize(a); + benchmark::DoNotOptimize(b); + benchmark::DoNotOptimize(c); + } +}; + +void BM_TrivialArgsStdFunction(benchmark::State& state) { + CallFunctionBenchmark>( + state, FunctorWithTrivialArgs{}, 1, 2, 3); +} +BENCHMARK(BM_TrivialArgsStdFunction); + +void BM_TrivialArgsFunctionRef(benchmark::State& state) { + CallFunctionBenchmark>( + state, FunctorWithTrivialArgs{}, 1, 2, 3); +} +BENCHMARK(BM_TrivialArgsFunctionRef); + +struct FunctorWithNonTrivialArgs { + void operator()(std::string a, std::string b, std::string c) const { + benchmark::DoNotOptimize(&a); + benchmark::DoNotOptimize(&b); + benchmark::DoNotOptimize(&c); + } +}; + +void BM_NonTrivialArgsStdFunction(benchmark::State& state) { + std::string a, b, c; + CallFunctionBenchmark< + std::function>( + state, FunctorWithNonTrivialArgs{}, a, b, c); +} +BENCHMARK(BM_NonTrivialArgsStdFunction); + +void BM_NonTrivialArgsFunctionRef(benchmark::State& state) { + std::string a, b, c; + CallFunctionBenchmark< + FunctionRef>( + state, FunctorWithNonTrivialArgs{}, a, b, c); +} +BENCHMARK(BM_NonTrivialArgsFunctionRef); + +} // namespace +} // namespace absl diff --git a/absl/functional/function_ref_test.cc b/absl/functional/function_ref_test.cc new file mode 100644 index 00000000000..90829db09a4 --- /dev/null +++ b/absl/functional/function_ref_test.cc @@ -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. + +#include "absl/functional/function_ref.h" + +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/container/internal/test_instance_tracker.h" +#include "absl/memory/memory.h" + +namespace absl { +namespace { + +void RunFun(FunctionRef f) { f(); } + +TEST(FunctionRefTest, Lambda) { + bool ran = false; + RunFun([&] { ran = true; }); + EXPECT_TRUE(ran); +} + +int Function() { return 1337; } + +TEST(FunctionRefTest, Function1) { + FunctionRef ref(&Function); + EXPECT_EQ(1337, ref()); +} + +TEST(FunctionRefTest, Function2) { + FunctionRef ref(Function); + EXPECT_EQ(1337, ref()); +} + +int NoExceptFunction() noexcept { return 1337; } + +// TODO(jdennett): Add a test for noexcept member functions. +TEST(FunctionRefTest, NoExceptFunction) { + FunctionRef ref(NoExceptFunction); + EXPECT_EQ(1337, ref()); +} + +TEST(FunctionRefTest, ForwardsArgs) { + auto l = [](std::unique_ptr i) { return *i; }; + FunctionRef)> ref(l); + EXPECT_EQ(42, ref(absl::make_unique(42))); +} + +TEST(FunctionRef, ReturnMoveOnly) { + auto l = [] { return absl::make_unique(29); }; + FunctionRef()> ref(l); + EXPECT_EQ(29, *ref()); +} + +TEST(FunctionRef, ManyArgs) { + auto l = [](int a, int b, int c) { return a + b + c; }; + FunctionRef ref(l); + EXPECT_EQ(6, ref(1, 2, 3)); +} + +TEST(FunctionRef, VoidResultFromNonVoidFunctor) { + bool ran = false; + auto l = [&]() -> int { + ran = true; + return 2; + }; + FunctionRef ref(l); + ref(); + EXPECT_TRUE(ran); +} + +TEST(FunctionRef, CastFromDerived) { + struct Base {}; + struct Derived : public Base {}; + + Derived d; + auto l1 = [&](Base* b) { EXPECT_EQ(&d, b); }; + FunctionRef ref1(l1); + ref1(&d); + + auto l2 = [&]() -> Derived* { return &d; }; + FunctionRef ref2(l2); + EXPECT_EQ(&d, ref2()); +} + +TEST(FunctionRef, VoidResultFromNonVoidFuncton) { + FunctionRef ref(Function); + ref(); +} + +TEST(FunctionRef, MemberPtr) { + struct S { + int i; + }; + + S s{1100111}; + auto mem_ptr = &S::i; + FunctionRef ref(mem_ptr); + EXPECT_EQ(1100111, ref(s)); +} + +TEST(FunctionRef, MemberFun) { + struct S { + int i; + int get_i() const { return i; } + }; + + S s{22}; + auto mem_fun_ptr = &S::get_i; + FunctionRef ref(mem_fun_ptr); + EXPECT_EQ(22, ref(s)); +} + +TEST(FunctionRef, MemberFunRefqualified) { + struct S { + int i; + int get_i() && { return i; } + }; + auto mem_fun_ptr = &S::get_i; + S s{22}; + FunctionRef ref(mem_fun_ptr); + EXPECT_EQ(22, ref(std::move(s))); +} + +#if !defined(_WIN32) && defined(GTEST_HAS_DEATH_TEST) + +TEST(FunctionRef, MemberFunRefqualifiedNull) { + struct S { + int i; + int get_i() && { return i; } + }; + auto mem_fun_ptr = &S::get_i; + mem_fun_ptr = nullptr; + EXPECT_DEBUG_DEATH({ FunctionRef ref(mem_fun_ptr); }, ""); +} + +TEST(FunctionRef, NullMemberPtrAssertFails) { + struct S { + int i; + }; + using MemberPtr = int S::*; + MemberPtr mem_ptr = nullptr; + EXPECT_DEBUG_DEATH({ FunctionRef ref(mem_ptr); }, ""); +} + +#endif // GTEST_HAS_DEATH_TEST + +TEST(FunctionRef, CopiesAndMovesPerPassByValue) { + absl::test_internal::InstanceTracker tracker; + absl::test_internal::CopyableMovableInstance instance(0); + auto l = [](absl::test_internal::CopyableMovableInstance) {}; + FunctionRef ref(l); + ref(instance); + EXPECT_EQ(tracker.copies(), 1); + EXPECT_EQ(tracker.moves(), 1); +} + +TEST(FunctionRef, CopiesAndMovesPerPassByRef) { + absl::test_internal::InstanceTracker tracker; + absl::test_internal::CopyableMovableInstance instance(0); + auto l = [](const absl::test_internal::CopyableMovableInstance&) {}; + FunctionRef ref(l); + ref(instance); + EXPECT_EQ(tracker.copies(), 0); + EXPECT_EQ(tracker.moves(), 0); +} + +TEST(FunctionRef, CopiesAndMovesPerPassByValueCallByMove) { + absl::test_internal::InstanceTracker tracker; + absl::test_internal::CopyableMovableInstance instance(0); + auto l = [](absl::test_internal::CopyableMovableInstance) {}; + FunctionRef ref(l); + ref(std::move(instance)); + EXPECT_EQ(tracker.copies(), 0); + EXPECT_EQ(tracker.moves(), 2); +} + +TEST(FunctionRef, CopiesAndMovesPerPassByValueToRef) { + absl::test_internal::InstanceTracker tracker; + absl::test_internal::CopyableMovableInstance instance(0); + auto l = [](const absl::test_internal::CopyableMovableInstance&) {}; + FunctionRef ref(l); + ref(std::move(instance)); + EXPECT_EQ(tracker.copies(), 0); + EXPECT_EQ(tracker.moves(), 1); +} + +TEST(FunctionRef, PassByValueTypes) { + using absl::functional_internal::Invoker; + using absl::functional_internal::VoidPtr; + using absl::test_internal::CopyableMovableInstance; + struct Trivial { + void* p[2]; + }; + struct LargeTrivial { + void* p[3]; + }; + + static_assert(std::is_same, void (*)(VoidPtr, int)>::value, + "Scalar types should be passed by value"); + static_assert( + std::is_same, void (*)(VoidPtr, Trivial)>::value, + "Small trivial types should be passed by value"); + static_assert(std::is_same, + void (*)(VoidPtr, LargeTrivial &&)>::value, + "Large trivial types should be passed by rvalue reference"); + static_assert( + std::is_same, + void (*)(VoidPtr, CopyableMovableInstance &&)>::value, + "Types with copy/move ctor should be passed by rvalue reference"); + + // References are passed as references. + static_assert( + std::is_same, void (*)(VoidPtr, int&)>::value, + "Reference types should be preserved"); + static_assert( + std::is_same, + void (*)(VoidPtr, CopyableMovableInstance&)>::value, + "Reference types should be preserved"); + static_assert( + std::is_same, + void (*)(VoidPtr, CopyableMovableInstance &&)>::value, + "Reference types should be preserved"); + + // Make sure the address of an object received by reference is the same as the + // addess of the object passed by the caller. + { + LargeTrivial obj; + auto test = [&obj](LargeTrivial& input) { ASSERT_EQ(&input, &obj); }; + absl::FunctionRef ref(test); + ref(obj); + } + + { + Trivial obj; + auto test = [&obj](Trivial& input) { ASSERT_EQ(&input, &obj); }; + absl::FunctionRef ref(test); + ref(obj); + } +} + +} // namespace +} // namespace absl diff --git a/absl/functional/internal/function_ref.h b/absl/functional/internal/function_ref.h new file mode 100644 index 00000000000..fcb0496c2c0 --- /dev/null +++ b/absl/functional/internal/function_ref.h @@ -0,0 +1,104 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_FUNCTIONAL_INTERNAL_FUNCTION_REF_H_ +#define ABSL_FUNCTIONAL_INTERNAL_FUNCTION_REF_H_ + +#include +#include +#include + +#include "absl/base/internal/invoke.h" +#include "absl/meta/type_traits.h" + +namespace absl { +namespace functional_internal { + +// Like a void* that can handle function pointers as well. The standard does not +// allow function pointers to round-trip through void*, but void(*)() is fine. +// +// Note: It's important that this class remains trivial and is the same size as +// a pointer, since this allows the compiler to perform tail-call optimizations +// when the underlying function is a callable object with a matching signature. +union VoidPtr { + const void* obj; + void (*fun)(); +}; + +// Chooses the best type for passing T as an argument. +// Attempt to be close to SystemV AMD64 ABI. Objects with trivial copy ctor are +// passed by value. +template +constexpr bool PassByValue() { + return !std::is_lvalue_reference::value && + absl::is_trivially_copy_constructible::value && + absl::is_trivially_copy_assignable< + typename std::remove_cv::type>::value && + std::is_trivially_destructible::value && + sizeof(T) <= 2 * sizeof(void*); +} + +template +struct ForwardT : std::conditional(), T, T&&> {}; + +// An Invoker takes a pointer to the type-erased invokable object, followed by +// the arguments that the invokable object expects. +// +// Note: The order of arguments here is an optimization, since member functions +// have an implicit "this" pointer as their first argument, putting VoidPtr +// first allows the compiler to perform tail-call optimization in many cases. +template +using Invoker = R (*)(VoidPtr, typename ForwardT::type...); + +// +// InvokeObject and InvokeFunction provide static "Invoke" functions that can be +// used as Invokers for objects or functions respectively. +// +// static_cast handles the case the return type is void. +template +R InvokeObject(VoidPtr ptr, typename ForwardT::type... args) { + auto o = static_cast(ptr.obj); + return static_cast( + absl::base_internal::Invoke(*o, std::forward(args)...)); +} + +template +R InvokeFunction(VoidPtr ptr, typename ForwardT::type... args) { + auto f = reinterpret_cast(ptr.fun); + return static_cast( + absl::base_internal::Invoke(f, std::forward(args)...)); +} + +template +void AssertNonNull(const std::function& f) { + assert(f != nullptr); + (void)f; +} + +template +void AssertNonNull(const F&) {} + +template +void AssertNonNull(F C::*f) { + assert(f != nullptr); + (void)f; +} + +template +using EnableIf = typename ::std::enable_if::type; + +} // namespace functional_internal +} // namespace absl + +#endif // ABSL_FUNCTIONAL_INTERNAL_FUNCTION_REF_H_ diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel index 4863ead2992..e38c8ad6ce6 100644 --- a/absl/strings/BUILD.bazel +++ b/absl/strings/BUILD.bazel @@ -413,8 +413,9 @@ cc_test( deps = [ ":pow10_helper", ":strings", - "//absl/base:core_headers", "//absl/base:raw_logging_internal", + "//absl/random", + "//absl/random:distributions", "@com_google_googletest//:gtest_main", ], ) @@ -428,6 +429,8 @@ cc_test( deps = [ ":strings", "//absl/base:raw_logging_internal", + "//absl/random", + "//absl/random:distributions", "@com_github_google_benchmark//:benchmark_main", ], ) diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt index 3f907957501..cd52a472f49 100644 --- a/absl/strings/CMakeLists.txt +++ b/absl/strings/CMakeLists.txt @@ -277,6 +277,8 @@ absl_cc_test( absl::core_headers absl::pow10_helper absl::raw_logging_internal + absl::random_random + absl::random_distributions gmock_main ) diff --git a/absl/strings/escaping.cc b/absl/strings/escaping.cc index 0d336e3ff69..88390fbfa82 100644 --- a/absl/strings/escaping.cc +++ b/absl/strings/escaping.cc @@ -35,27 +35,6 @@ namespace absl { namespace { -// Digit conversion. -constexpr char kHexChar[] = "0123456789abcdef"; - -constexpr char kHexTable[513] = - "000102030405060708090a0b0c0d0e0f" - "101112131415161718191a1b1c1d1e1f" - "202122232425262728292a2b2c2d2e2f" - "303132333435363738393a3b3c3d3e3f" - "404142434445464748494a4b4c4d4e4f" - "505152535455565758595a5b5c5d5e5f" - "606162636465666768696a6b6c6d6e6f" - "707172737475767778797a7b7c7d7e7f" - "808182838485868788898a8b8c8d8e8f" - "909192939495969798999a9b9c9d9e9f" - "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf" - "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf" - "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf" - "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf" - "e0e1e2e3e4e5e6e7e8e9eaebecedeeef" - "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"; - // These are used for the leave_nulls_escaped argument to CUnescapeInternal(). constexpr bool kUnescapeNulls = false; @@ -348,14 +327,14 @@ std::string CEscapeInternal(absl::string_view src, bool use_hex, (last_hex_escape && absl::ascii_isxdigit(c)))) { if (use_hex) { dest.append("\\" "x"); - dest.push_back(kHexChar[c / 16]); - dest.push_back(kHexChar[c % 16]); + dest.push_back(numbers_internal::kHexChar[c / 16]); + dest.push_back(numbers_internal::kHexChar[c % 16]); is_hex_escape = true; } else { dest.append("\\"); - dest.push_back(kHexChar[c / 64]); - dest.push_back(kHexChar[(c % 64) / 8]); - dest.push_back(kHexChar[c % 8]); + dest.push_back(numbers_internal::kHexChar[c / 64]); + dest.push_back(numbers_internal::kHexChar[(c % 64) / 8]); + dest.push_back(numbers_internal::kHexChar[c % 8]); } } else { dest.push_back(c); @@ -1019,7 +998,7 @@ template void BytesToHexStringInternal(const unsigned char* src, T dest, ptrdiff_t num) { auto dest_ptr = &dest[0]; for (auto src_ptr = src; src_ptr != (src + num); ++src_ptr, dest_ptr += 2) { - const char* hex_p = &kHexTable[*src_ptr * 2]; + const char* hex_p = &numbers_internal::kHexTable[*src_ptr * 2]; std::copy(hex_p, hex_p + 2, dest_ptr); } } diff --git a/absl/strings/numbers.cc b/absl/strings/numbers.cc index 38d148690e2..d7b94fc1301 100644 --- a/absl/strings/numbers.cc +++ b/absl/strings/numbers.cc @@ -19,8 +19,8 @@ #include #include -#include // for DBL_DIG and FLT_DIG -#include // for HUGE_VAL +#include // for DBL_DIG and FLT_DIG +#include // for HUGE_VAL #include #include #include @@ -34,6 +34,7 @@ #include "absl/base/internal/raw_logging.h" #include "absl/strings/ascii.h" #include "absl/strings/charconv.h" +#include "absl/strings/escaping.h" #include "absl/strings/internal/memutil.h" #include "absl/strings/match.h" #include "absl/strings/str_cat.h" @@ -885,6 +886,28 @@ inline bool safe_uint_internal(absl::string_view text, IntType* value_p, } // anonymous namespace namespace numbers_internal { + +// Digit conversion. +ABSL_CONST_INIT const char kHexChar[] = "0123456789abcdef"; + +ABSL_CONST_INIT const char kHexTable[513] = + "000102030405060708090a0b0c0d0e0f" + "101112131415161718191a1b1c1d1e1f" + "202122232425262728292a2b2c2d2e2f" + "303132333435363738393a3b3c3d3e3f" + "404142434445464748494a4b4c4d4e4f" + "505152535455565758595a5b5c5d5e5f" + "606162636465666768696a6b6c6d6e6f" + "707172737475767778797a7b7c7d7e7f" + "808182838485868788898a8b8c8d8e8f" + "909192939495969798999a9b9c9d9e9f" + "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf" + "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf" + "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf" + "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf" + "e0e1e2e3e4e5e6e7e8e9eaebecedeeef" + "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"; + bool safe_strto32_base(absl::string_view text, int32_t* value, int base) { return safe_int_internal(text, value, base); } @@ -900,6 +923,6 @@ bool safe_strtou32_base(absl::string_view text, uint32_t* value, int base) { bool safe_strtou64_base(absl::string_view text, uint64_t* value, int base) { return safe_uint_internal(text, value, base); } -} // namespace numbers_internal +} // namespace numbers_internal } // namespace absl diff --git a/absl/strings/numbers.h b/absl/strings/numbers.h index 100839b0462..745de67ad86 100644 --- a/absl/strings/numbers.h +++ b/absl/strings/numbers.h @@ -24,6 +24,10 @@ #ifndef ABSL_STRINGS_NUMBERS_H_ #define ABSL_STRINGS_NUMBERS_H_ +#ifdef __SSE4_2__ +#include +#endif + #include #include #include @@ -32,6 +36,8 @@ #include #include +#include "absl/base/internal/bits.h" +#include "absl/base/internal/endian.h" #include "absl/base/macros.h" #include "absl/base/port.h" #include "absl/numeric/int128.h" @@ -54,7 +60,8 @@ ABSL_MUST_USE_RESULT bool SimpleAtoi(absl::string_view str, int_type* out); // Converts the given string (optionally followed or preceded by ASCII // whitespace) into a float, which may be rounded on overflow or underflow. // See https://en.cppreference.com/w/c/string/byte/strtof for details about the -// allowed formats for `str`. If any errors are encountered, this function +// allowed formats for `str`, except SimpleAtof() is locale-indepdent and will +// always use the "C" locale. If any errors are encountered, this function // returns `false`, leaving `out` in an unspecified state. ABSL_MUST_USE_RESULT bool SimpleAtof(absl::string_view str, float* out); @@ -63,7 +70,8 @@ ABSL_MUST_USE_RESULT bool SimpleAtof(absl::string_view str, float* out); // Converts the given string (optionally followed or preceded by ASCII // whitespace) into a double, which may be rounded on overflow or underflow. // See https://en.cppreference.com/w/c/string/byte/strtof for details about the -// allowed formats for `str`. If any errors are encountered, this function +// allowed formats for `str`, except SimpleAtod is locale-independent and will +// always use the "C" locale. If any errors are encountered, this function // returns `false`, leaving `out` in an unspecified state. ABSL_MUST_USE_RESULT bool SimpleAtod(absl::string_view str, double* out); @@ -84,6 +92,10 @@ ABSL_MUST_USE_RESULT bool SimpleAtob(absl::string_view str, bool* out); namespace absl { namespace numbers_internal { +// Digit conversion. +extern const char kHexChar[17]; // 0123456789abcdef +extern const char kHexTable[513]; // 000102030405060708090a0b0c0d0e0f1011... + // safe_strto?() functions for implementing SimpleAtoi() bool safe_strto32_base(absl::string_view text, int32_t* value, int base); bool safe_strto64_base(absl::string_view text, int64_t* value, int base); @@ -170,6 +182,35 @@ ABSL_MUST_USE_RESULT bool safe_strtoi_base(absl::string_view s, int_type* out, return parsed; } +// FastHexToBufferZeroPad16() +// +// Outputs `val` into `out` as if by `snprintf(out, 17, "%016x", val)` but +// without the terminating null character. Thus `out` must be of length >= 16. +// Returns the number of non-pad digits of the output (it can never be zero +// since 0 has one digit). +inline size_t FastHexToBufferZeroPad16(uint64_t val, char* out) { + uint64_t be = absl::big_endian::FromHost64(val); +#ifdef __SSE4_2__ + const auto kNibbleMask = _mm_set1_epi8(0xf); + const auto kHexDigits = _mm_setr_epi8('0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'); + auto v = _mm_loadu_si64(reinterpret_cast<__m128i*>(&be)); // load lo dword + auto v4 = _mm_srli_epi64(v, 4); // shift 4 right + auto il = _mm_unpacklo_epi8(v4, v); // interleave bytes + auto m = _mm_and_si128(il, kNibbleMask); // mask out nibbles + auto hexchars = _mm_shuffle_epi8(kHexDigits, m); // hex chars + _mm_storeu_si128(reinterpret_cast<__m128i*>(out), hexchars); +#else + for (int i = 0; i < 8; ++i) { + auto byte = (be >> (8 * i)) & 0xFF; + auto* hex = &absl::numbers_internal::kHexTable[byte * 2]; + std::memcpy(out + 2 * i, hex, 2); + } +#endif + // | 0x1 so that even 0 has 1 digit. + return 16 - absl::base_internal::CountLeadingZeros64(val | 0x1) / 4; +} + } // namespace numbers_internal // SimpleAtoi() diff --git a/absl/strings/numbers_benchmark.cc b/absl/strings/numbers_benchmark.cc index 54dbedd3535..6e79b3e811f 100644 --- a/absl/strings/numbers_benchmark.cc +++ b/absl/strings/numbers_benchmark.cc @@ -20,6 +20,8 @@ #include "benchmark/benchmark.h" #include "absl/base/internal/raw_logging.h" +#include "absl/random/distributions.h" +#include "absl/random/random.h" #include "absl/strings/numbers.h" namespace { @@ -260,4 +262,25 @@ BENCHMARK_TEMPLATE(BM_SimpleAtod, std::string) ->ArgPair(10, 4) ->ArgPair(10, 8); +void BM_FastHexToBufferZeroPad16(benchmark::State& state) { + absl::BitGen rng; + std::vector nums; + nums.resize(1000); + auto min = std::numeric_limits::min(); + auto max = std::numeric_limits::max(); + for (auto& num : nums) { + num = absl::LogUniform(rng, min, max); + } + + char buf[16]; + while (state.KeepRunningBatch(nums.size())) { + for (auto num : nums) { + auto digits = absl::numbers_internal::FastHexToBufferZeroPad16(num, buf); + benchmark::DoNotOptimize(digits); + benchmark::DoNotOptimize(buf); + } + } +} +BENCHMARK(BM_FastHexToBufferZeroPad16); + } // namespace diff --git a/absl/strings/numbers_test.cc b/absl/strings/numbers_test.cc index d964e562b98..b92b9a8cb2a 100644 --- a/absl/strings/numbers_test.cc +++ b/absl/strings/numbers_test.cc @@ -17,6 +17,7 @@ #include "absl/strings/numbers.h" #include + #include // NOLINT(build/c++11) #include #include @@ -36,10 +37,11 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/base/internal/raw_logging.h" -#include "absl/strings/str_cat.h" - +#include "absl/random/distributions.h" +#include "absl/random/random.h" #include "absl/strings/internal/numbers_test_common.h" #include "absl/strings/internal/pow10_helper.h" +#include "absl/strings/str_cat.h" namespace { @@ -1187,4 +1189,28 @@ TEST(StrToUint64Base, PrefixOnly) { } } +void TestFastHexToBufferZeroPad16(uint64_t v) { + char buf[16]; + auto digits = absl::numbers_internal::FastHexToBufferZeroPad16(v, buf); + absl::string_view res(buf, 16); + char buf2[17]; + snprintf(buf2, sizeof(buf2), "%016" PRIx64, v); + EXPECT_EQ(res, buf2) << v; + size_t expected_digits = snprintf(buf2, sizeof(buf2), "%" PRIx64, v); + EXPECT_EQ(digits, expected_digits) << v; +} + +TEST(FastHexToBufferZeroPad16, Smoke) { + TestFastHexToBufferZeroPad16(std::numeric_limits::min()); + TestFastHexToBufferZeroPad16(std::numeric_limits::max()); + TestFastHexToBufferZeroPad16(std::numeric_limits::min()); + TestFastHexToBufferZeroPad16(std::numeric_limits::max()); + absl::BitGen rng; + for (int i = 0; i < 100000; ++i) { + TestFastHexToBufferZeroPad16( + absl::LogUniform(rng, std::numeric_limits::min(), + std::numeric_limits::max())); + } +} + } // namespace diff --git a/absl/strings/str_cat.cc b/absl/strings/str_cat.cc index 996194457fd..d587789972e 100644 --- a/absl/strings/str_cat.cc +++ b/absl/strings/str_cat.cc @@ -15,12 +15,14 @@ #include "absl/strings/str_cat.h" #include + #include #include #include #include "absl/strings/ascii.h" #include "absl/strings/internal/resize_uninitialized.h" +#include "absl/strings/numbers.h" namespace absl { @@ -28,9 +30,8 @@ AlphaNum::AlphaNum(Hex hex) { char* const end = &digits_[numbers_internal::kFastToBufferSize]; char* writer = end; uint64_t value = hex.value; - static const char hexdigits[] = "0123456789abcdef"; do { - *--writer = hexdigits[value & 0xF]; + *--writer = absl::numbers_internal::kHexChar[value & 0xF]; value >>= 4; } while (value != 0); diff --git a/absl/strings/substitute.cc b/absl/strings/substitute.cc index bc176950e45..36dbfe7d42f 100644 --- a/absl/strings/substitute.cc +++ b/absl/strings/substitute.cc @@ -94,7 +94,6 @@ void SubstituteAndAppendArray(std::string* output, absl::string_view format, assert(target == output->data() + output->size()); } -static const char kHexDigits[] = "0123456789abcdef"; Arg::Arg(const void* value) { static_assert(sizeof(scratch_) >= sizeof(value) * 2 + 2, "fix sizeof(scratch_)"); @@ -104,7 +103,7 @@ Arg::Arg(const void* value) { char* ptr = scratch_ + sizeof(scratch_); uintptr_t num = reinterpret_cast(value); do { - *--ptr = kHexDigits[num & 0xf]; + *--ptr = absl::numbers_internal::kHexChar[num & 0xf]; num >>= 4; } while (num != 0); *--ptr = 'x'; @@ -119,7 +118,7 @@ Arg::Arg(Hex hex) { char* writer = end; uint64_t value = hex.value; do { - *--writer = kHexDigits[value & 0xF]; + *--writer = absl::numbers_internal::kHexChar[value & 0xF]; value >>= 4; } while (value != 0); diff --git a/ci/linux_gcc_alpine_cmake.sh b/ci/linux_gcc_alpine_cmake.sh new file mode 100755 index 00000000000..830a1360a37 --- /dev/null +++ b/ci/linux_gcc_alpine_cmake.sh @@ -0,0 +1,63 @@ +#!/bin/bash +# +# Copyright 2019 The Abseil Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# TODO(absl-team): This script isn't fully hermetic because +# -DABSL_USE_GOOGLETEST_HEAD=ON means that this script isn't pinned to a fixed +# version of GoogleTest. This means that an upstream change to GoogleTest could +# break this test. Fix this by allowing this script to pin to a known-good +# version of GoogleTest. + +set -euox pipefail + +if [ -z ${ABSEIL_ROOT:-} ]; then + ABSEIL_ROOT="$(realpath $(dirname ${0})/..)" +fi + +if [ -z ${ABSL_CMAKE_CXX_STANDARDS:-} ]; then + ABSL_CMAKE_CXX_STANDARDS="11 14 17" +fi + +if [ -z ${ABSL_CMAKE_BUILD_TYPES:-} ]; then + ABSL_CMAKE_BUILD_TYPES="Debug Release" +fi + +readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/alpine:20191016" + +for std in ${ABSL_CMAKE_CXX_STANDARDS}; do + for compilation_mode in ${ABSL_CMAKE_BUILD_TYPES}; do + echo "--------------------------------------------------------------------" + echo "Testing with CMAKE_BUILD_TYPE=${compilation_mode} and -std=c++${std}" + + time docker run \ + --volume="${ABSEIL_ROOT}:/abseil-cpp:ro" \ + --workdir=/abseil-cpp \ + --tmpfs=/buildfs:exec \ + --cap-add=SYS_PTRACE \ + --rm \ + -e CFLAGS="-Werror" \ + -e CXXFLAGS="-Werror" \ + "${DOCKER_CONTAINER}" \ + /bin/sh -c " + cd /buildfs && \ + cmake /abseil-cpp \ + -DABSL_USE_GOOGLETEST_HEAD=ON \ + -DABSL_RUN_TESTS=ON \ + -DCMAKE_BUILD_TYPE=${compilation_mode} \ + -DCMAKE_CXX_STANDARD=${std} && \ + make -j$(nproc) && \ + ctest -j$(nproc) --output-on-failure" + done +done From 2796d500aea5a31d26b8b24a33fab7a1c8fa2f32 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Mon, 21 Oct 2019 10:21:03 -0700 Subject: [PATCH 0007/3164] Export of internal Abseil changes -- b770d03c2f1042d399c3f9576e881691cbe962c4 by Abseil Team : Avoid applying the workaround for MSVC's static initialization problems when using clang-cl. PiperOrigin-RevId: 275870089 -- 2b8fc02ec49aeb5ad56cef20259cdd7f6ee0c917 by Abseil Team : Document return values. PiperOrigin-RevId: 275839042 -- baa99064f9a28b188661df7fba989fcd558818af by Abseil Team : Support "auto" and other uncommon builtin types in absl::debugging_internal::Demangle. PiperOrigin-RevId: 275556195 -- f9d5c6a3a0d374dbf105d6e2e9e0c8fa949ed187 by Gennadiy Rozental : Internal rework. PiperOrigin-RevId: 275550005 -- 2679a77db5b26349e8c8b2059621af55d2fca139 by Mark Barolak : Remove a comment reference to the no longer extant ::string implementation. PiperOrigin-RevId: 275531987 -- 7b427a7613c44a98c6f13da43b2bff2837ca6b19 by Derek Mauro : Upgrade to Bazel 1.0.0 and CMake 3.15.4 PiperOrigin-RevId: 275500823 -- 81f7d20905debf9d1e300bd2e9899f88d27f632a by Derek Mauro : Fix -Wimplicit-int-float-conversion warning in latest clang PiperOrigin-RevId: 275492439 GitOrigin-RevId: b770d03c2f1042d399c3f9576e881691cbe962c4 Change-Id: I9b39dad524489f0d62c912d02e8ac43761c81e55 --- absl/debugging/internal/demangle.cc | 34 +- absl/debugging/stacktrace.h | 30 +- absl/flags/BUILD.bazel | 4 +- absl/flags/declare.h | 2 +- absl/flags/flag.cc | 2 +- absl/flags/flag.h | 10 +- absl/flags/flag_test.cc | 9 +- absl/flags/internal/commandlineflag.cc | 333 ----------------- absl/flags/internal/commandlineflag.h | 187 ++++------ absl/flags/internal/flag.cc | 337 ++++++++++++++++-- absl/flags/internal/flag.h | 237 ++++++++---- absl/flags/internal/registry.cc | 45 ++- absl/flags/internal/registry.h | 6 +- .../internal/iostream_state_saver_test.cc | 8 +- absl/strings/internal/resize_uninitialized.h | 3 +- ci/cmake_install_test.sh | 3 +- ci/linux_clang-latest_libcxx_asan_bazel.sh | 2 +- ci/linux_clang-latest_libcxx_bazel.sh | 2 +- ci/linux_clang-latest_libcxx_tsan_bazel.sh | 2 +- ci/linux_clang-latest_libstdcxx_bazel.sh | 2 +- ci/linux_gcc-4.9_libstdcxx_bazel.sh | 2 +- ci/linux_gcc-latest_libstdcxx_bazel.sh | 2 +- ci/linux_gcc-latest_libstdcxx_cmake.sh | 2 +- 23 files changed, 634 insertions(+), 630 deletions(-) diff --git a/absl/debugging/internal/demangle.cc b/absl/debugging/internal/demangle.cc index 52a553fd058..3809e4960e2 100644 --- a/absl/debugging/internal/demangle.cc +++ b/absl/debugging/internal/demangle.cc @@ -93,6 +93,8 @@ static const AbbrevPair kOperatorList[] = { }; // List of builtin types from Itanium C++ ABI. +// +// Invariant: only one- or two-character type abbreviations here. static const AbbrevPair kBuiltinTypeList[] = { {"v", "void", 0}, {"w", "wchar_t", 0}, @@ -115,6 +117,16 @@ static const AbbrevPair kBuiltinTypeList[] = { {"e", "long double", 0}, {"g", "__float128", 0}, {"z", "ellipsis", 0}, + + {"De", "decimal128", 0}, // IEEE 754r decimal floating point (128 bits) + {"Dd", "decimal64", 0}, // IEEE 754r decimal floating point (64 bits) + {"Dc", "decltype(auto)", 0}, + {"Da", "auto", 0}, + {"Dn", "std::nullptr_t", 0}, // i.e., decltype(nullptr) + {"Df", "decimal32", 0}, // IEEE 754r decimal floating point (32 bits) + {"Di", "char32_t", 0}, + {"Ds", "char16_t", 0}, + {"Dh", "float16", 0}, // IEEE 754r half-precision float (16 bits) {nullptr, nullptr, 0}, }; @@ -1168,12 +1180,6 @@ static bool ParseType(State *state) { } state->parse_state = copy; - // nullptr_t, i.e. decltype(nullptr). - if (ParseTwoCharToken(state, "Dn")) { - return true; - } - state->parse_state = copy; - if (ParseOneCharToken(state, 'U') && ParseSourceName(state) && ParseType(state)) { return true; @@ -1214,16 +1220,26 @@ static bool ParseCVQualifiers(State *state) { return num_cv_qualifiers > 0; } -// ::= v, etc. +// ::= v, etc. # single-character builtin types // ::= u +// ::= Dd, etc. # two-character builtin types +// +// Not supported: +// ::= DF _ # _FloatN (N bits) +// static bool ParseBuiltinType(State *state) { ComplexityGuard guard(state); if (guard.IsTooComplex()) return false; const AbbrevPair *p; for (p = kBuiltinTypeList; p->abbrev != nullptr; ++p) { - if (RemainingInput(state)[0] == p->abbrev[0]) { + // Guaranteed only 1- or 2-character strings in kBuiltinTypeList. + if (p->abbrev[1] == '\0') { + if (ParseOneCharToken(state, p->abbrev[0])) { + MaybeAppend(state, p->real_name); + return true; + } + } else if (p->abbrev[2] == '\0' && ParseTwoCharToken(state, p->abbrev)) { MaybeAppend(state, p->real_name); - ++state->parse_state.mangled_idx; return true; } } diff --git a/absl/debugging/stacktrace.h b/absl/debugging/stacktrace.h index 3fc1c03f3e6..cd8fae76d53 100644 --- a/absl/debugging/stacktrace.h +++ b/absl/debugging/stacktrace.h @@ -36,10 +36,10 @@ namespace absl { // GetStackFrames() // // Records program counter values for up to `max_depth` frames, skipping the -// most recent `skip_count` stack frames, and stores their corresponding values -// and sizes in `results` and `sizes` buffers. (Note that the frame generated -// for the `absl::GetStackFrames()` routine itself is also skipped.) -// routine itself. +// most recent `skip_count` stack frames, stores their corresponding values +// and sizes in `results` and `sizes` buffers, and returns the number of frames +// stored. (Note that the frame generated for the `absl::GetStackFrames()` +// routine itself is also skipped.) // // Example: // @@ -54,8 +54,8 @@ namespace absl { // The current stack frame would consist of three function calls: `bar()`, // `foo()`, and then `main()`; however, since the `GetStackFrames()` call sets // `skip_count` to `1`, it will skip the frame for `bar()`, the most recently -// invoked function call. It will therefore return two program counters and will -// produce values that map to the following function calls: +// invoked function call. It will therefore return 2 and fill `result` with +// program counters within the following functions: // // result[0] foo() // result[1] main() @@ -82,9 +82,10 @@ extern int GetStackFrames(void** result, int* sizes, int max_depth, // // Records program counter values obtained from a signal handler. Records // program counter values for up to `max_depth` frames, skipping the most recent -// `skip_count` stack frames, and stores their corresponding values and sizes in -// `results` and `sizes` buffers. (Note that the frame generated for the -// `absl::GetStackFramesWithContext()` routine itself is also skipped.) +// `skip_count` stack frames, stores their corresponding values and sizes in +// `results` and `sizes` buffers, and returns the number of frames stored. (Note +// that the frame generated for the `absl::GetStackFramesWithContext()` routine +// itself is also skipped.) // // The `uc` parameter, if non-null, should be a pointer to a `ucontext_t` value // passed to a signal handler registered via the `sa_sigaction` field of a @@ -105,8 +106,9 @@ extern int GetStackFramesWithContext(void** result, int* sizes, int max_depth, // GetStackTrace() // // Records program counter values for up to `max_depth` frames, skipping the -// most recent `skip_count` stack frames, and stores their corresponding values -// in `results`. Note that this function is similar to `absl::GetStackFrames()` +// most recent `skip_count` stack frames, stores their corresponding values +// in `results`, and returns the number of frames +// stored. Note that this function is similar to `absl::GetStackFrames()` // except that it returns the stack trace only, and not stack frame sizes. // // Example: @@ -131,9 +133,9 @@ extern int GetStackTrace(void** result, int max_depth, int skip_count); // // Records program counter values obtained from a signal handler. Records // program counter values for up to `max_depth` frames, skipping the most recent -// `skip_count` stack frames, and stores their corresponding values in -// `results`. (Note that the frame generated for the -// `absl::GetStackFramesWithContext()` routine itself is also skipped.) +// `skip_count` stack frames, stores their corresponding values in `results`, +// and returns the number of frames stored. (Note that the frame generated for +// the `absl::GetStackFramesWithContext()` routine itself is also skipped.) // // The `uc` parameter, if non-null, should be a pointer to a `ucontext_t` value // passed to a signal handler registered via the `sa_sigaction` field of a diff --git a/absl/flags/BUILD.bazel b/absl/flags/BUILD.bazel index 2e0dc3891f5..cf449198554 100644 --- a/absl/flags/BUILD.bazel +++ b/absl/flags/BUILD.bazel @@ -40,6 +40,7 @@ cc_library( deps = [ ":handle", ":registry", + "//absl/base:core_headers", "//absl/memory", "//absl/strings", "//absl/synchronization", @@ -135,9 +136,6 @@ cc_library( ":config", ":marshalling", "//absl/base:core_headers", - "//absl/base:raw_logging_internal", - "//absl/strings", - "//absl/synchronization", "//absl/types:optional", ], ) diff --git a/absl/flags/declare.h b/absl/flags/declare.h index 0a113a2b27a..4926a09ef4d 100644 --- a/absl/flags/declare.h +++ b/absl/flags/declare.h @@ -39,7 +39,7 @@ class Flag; // Flag // // Forward declaration of the `absl::Flag` type for use in defining the macro. -#if defined(_MSC_VER) +#if defined(_MSC_VER) && !defined(__clang__) template class Flag; #else diff --git a/absl/flags/flag.cc b/absl/flags/flag.cc index 69038bbf48a..37bbce23311 100644 --- a/absl/flags/flag.cc +++ b/absl/flags/flag.cc @@ -43,7 +43,7 @@ ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(ABSL_FLAGS_ATOMIC_GET) // This global nutex protects on-demand construction of flag objects in MSVC // builds. -#if defined(_MSC_VER) +#if defined(_MSC_VER) && !defined(__clang__) namespace flags_internal { diff --git a/absl/flags/flag.h b/absl/flags/flag.h index 85900ef81b3..c0060b449a8 100644 --- a/absl/flags/flag.h +++ b/absl/flags/flag.h @@ -63,7 +63,7 @@ namespace absl { // ABSL_FLAG(int, count, 0, "Count of items to process"); // // No public methods of `absl::Flag` are part of the Abseil Flags API. -#if !defined(_MSC_VER) +#if !defined(_MSC_VER) || defined(__clang__) template using Flag = flags_internal::Flag; #else @@ -119,7 +119,6 @@ class Flag { absl::string_view Name() const { return GetImpl()->Name(); } std::string Help() const { return GetImpl()->Help(); } bool IsModified() const { return GetImpl()->IsModified(); } - void SetModified(bool is_modified) { GetImpl()->SetModified(is_modified); } bool IsSpecifiedOnCommandLine() const { return GetImpl()->IsSpecifiedOnCommandLine(); } @@ -127,9 +126,6 @@ class Flag { std::string Filename() const { return GetImpl()->Filename(); } std::string DefaultValue() const { return GetImpl()->DefaultValue(); } std::string CurrentValue() const { return GetImpl()->CurrentValue(); } - bool InvokeValidator(const void* value) const { - return GetImpl()->InvokeValidator(value); - } template inline bool IsOfType() const { return GetImpl()->template IsOfType(); @@ -272,7 +268,7 @@ void SetFlag(absl::Flag* flag, const V& v) { #if ABSL_FLAGS_STRIP_NAMES #define ABSL_FLAG_IMPL_FLAGNAME(txt) "" #define ABSL_FLAG_IMPL_FILENAME() "" -#if !defined(_MSC_VER) +#if !defined(_MSC_VER) || defined(__clang__) #define ABSL_FLAG_IMPL_REGISTRAR(T, flag) \ absl::flags_internal::FlagRegistrar(&flag) #else @@ -282,7 +278,7 @@ void SetFlag(absl::Flag* flag, const V& v) { #else #define ABSL_FLAG_IMPL_FLAGNAME(txt) txt #define ABSL_FLAG_IMPL_FILENAME() __FILE__ -#if !defined(_MSC_VER) +#if !defined(_MSC_VER) || defined(__clang__) #define ABSL_FLAG_IMPL_REGISTRAR(T, flag) \ absl::flags_internal::FlagRegistrar(&flag) #else diff --git a/absl/flags/flag_test.cc b/absl/flags/flag_test.cc index a652042c8aa..59dc579ce02 100644 --- a/absl/flags/flag_test.cc +++ b/absl/flags/flag_test.cc @@ -42,15 +42,14 @@ void TestCallback() {} template bool TestConstructionFor() { constexpr flags::Flag f1("f1", &TestHelpMsg, "file", - &absl::flags_internal::FlagMarshallingOps, - &TestMakeDflt); + &flags::FlagMarshallingOps, &TestMakeDflt); EXPECT_EQ(f1.Name(), "f1"); EXPECT_EQ(f1.Help(), "help"); EXPECT_EQ(f1.Filename(), "file"); - ABSL_CONST_INIT static flags::Flag f2( - "f2", &TestHelpMsg, "file", &absl::flags_internal::FlagMarshallingOps, - &TestMakeDflt); + ABSL_CONST_INIT static flags::Flag f2("f2", &TestHelpMsg, "file", + &flags::FlagMarshallingOps, + &TestMakeDflt); flags::FlagRegistrar(&f2).OnUpdate(TestCallback); EXPECT_EQ(f2.Name(), "f2"); diff --git a/absl/flags/internal/commandlineflag.cc b/absl/flags/internal/commandlineflag.cc index 99f73611d8f..caf33bc4892 100644 --- a/absl/flags/internal/commandlineflag.cc +++ b/absl/flags/internal/commandlineflag.cc @@ -15,14 +15,7 @@ #include "absl/flags/internal/commandlineflag.h" -#include - -#include "absl/base/internal/raw_logging.h" -#include "absl/base/optimization.h" -#include "absl/flags/config.h" #include "absl/flags/usage_config.h" -#include "absl/strings/str_cat.h" -#include "absl/synchronization/mutex.h" namespace absl { namespace flags_internal { @@ -35,80 +28,6 @@ namespace flags_internal { // This is used by this file, and also in commandlineflags_reporting.cc const char kStrippedFlagHelp[] = "\001\002\003\004 (unknown) \004\003\002\001"; -namespace { - -// Currently we only validate flag values for user-defined flag types. -bool ShouldValidateFlagValue(const CommandLineFlag& flag) { -#define DONT_VALIDATE(T) \ - if (flag.IsOfType()) return false; - ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(DONT_VALIDATE) - DONT_VALIDATE(std::string) - DONT_VALIDATE(std::vector) -#undef DONT_VALIDATE - - return true; -} - -} // namespace - -absl::Mutex* InitFlag(CommandLineFlag* flag) { - ABSL_CONST_INIT static absl::Mutex init_lock(absl::kConstInit); - absl::Mutex* mu; - - { - absl::MutexLock lock(&init_lock); - - if (flag->locks_ == nullptr) { // Must initialize Mutexes for this flag. - flag->locks_ = new flags_internal::CommandLineFlagLocks; - } - - mu = &flag->locks_->primary_mu; - } - - { - absl::MutexLock lock(mu); - - if (!flag->IsRetired() && 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 - ABSL_LOCK_RETURNED(locks_->primary_mu) { - if (!inited_.load(std::memory_order_acquire)) { - return InitFlag(const_cast(this)); - } - - // All fields initialized; locks_ is therefore safe to read. - return &locks_->primary_mu; -} - -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 @@ -137,223 +56,6 @@ std::string CommandLineFlag::Filename() const { return flags_internal::GetUsageConfig().normalize_filename(filename_); } -std::string CommandLineFlag::DefaultValue() const { - absl::MutexLock l(InitFlagIfNecessary()); - - return Unparse(marshalling_op_, def_); -} - -std::string CommandLineFlag::CurrentValue() const { - absl::MutexLock l(InitFlagIfNecessary()); - - return Unparse(marshalling_op_, cur_); -} - -int64_t CommandLineFlag::MutationCounter() const { - absl::MutexLock l(InitFlagIfNecessary()); - - return counter_; -} - -// 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) - ABSL_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 (!modified_ && ChangedDirectly(this, cur_, def_)) { - modified_ = true; - } - } - - switch (set_mode) { - case SET_FLAGS_VALUE: { - // set or modify the flag's value - if (!TryParseLocked(this, cur_, value, err)) return false; - modified_ = true; - UpdateCopy(this); - InvokeCallback(); - - if (source == kCommandLine) { - on_command_line_ = true; - } - break; - } - case SET_FLAG_IF_DEFAULT: { - // set the flag's value, but only if it hasn't been set by someone else - if (!modified_) { - if (!TryParseLocked(this, cur_, value, err)) return false; - 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(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, def_, value, err)) return false; - - if (!modified_) { - // Need to set both defvalue *and* current, in this case - Copy(op_, def_, cur_); - UpdateCopy(this); - InvokeCallback(); - } - break; - } - default: { - // unknown set_mode - assert(false); - return false; - } - } - - return true; -} - -void CommandLineFlag::CheckDefaultValueParsingRoundtrip() const { - std::string v = DefaultValue(); - - absl::MutexLock lock(InitFlagIfNecessary()); - - void* dst = Clone(op_, def_); - std::string error; - if (!flags_internal::Parse(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(op_, dst); -} - -bool CommandLineFlag::ValidateDefaultValue() const { - absl::MutexLock lock(InitFlagIfNecessary()); - return InvokeValidator(def_); -} - -bool CommandLineFlag::ValidateInputValue(absl::string_view value) const { - absl::MutexLock l(InitFlagIfNecessary()); // protect default value access - - void* obj = Clone(op_, def_); - std::string ignored_error; - const bool result = - flags_internal::Parse(marshalling_op_, value, obj, &ignored_error) && - InvokeValidator(obj); - Delete(op_, obj); - return result; -} - -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_; @@ -361,40 +63,5 @@ std::string HelpText::GetHelpText() const { return {}; } -// Update any copy of the flag value that is stored in an atomic word. -// In addition if flag has a mutation callback this function invokes it. -void UpdateCopy(CommandLineFlag* flag) { -#define STORE_ATOMIC(T) \ - else if (flag->IsOfType()) { \ - flag->StoreAtomic(); \ - } - - if (false) { - } - ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(STORE_ATOMIC) -#undef STORE_ATOMIC -} - -// Return true iff flag value was changed via direct-access. -bool ChangedDirectly(CommandLineFlag* flag, const void* a, const void* b) { - if (!flag->IsAbseilFlag()) { -// Need to compare values for direct-access flags. -#define CHANGED_FOR_TYPE(T) \ - if (flag->IsOfType()) { \ - return *reinterpret_cast(a) != *reinterpret_cast(b); \ - } - - CHANGED_FOR_TYPE(bool); - CHANGED_FOR_TYPE(int32_t); - CHANGED_FOR_TYPE(int64_t); - CHANGED_FOR_TYPE(uint64_t); - CHANGED_FOR_TYPE(double); - CHANGED_FOR_TYPE(std::string); -#undef CHANGED_FOR_TYPE - } - - return false; -} - } // namespace flags_internal } // namespace absl diff --git a/absl/flags/internal/commandlineflag.h b/absl/flags/internal/commandlineflag.h index 528d3106bfa..13a3352e80f 100644 --- a/absl/flags/internal/commandlineflag.h +++ b/absl/flags/internal/commandlineflag.h @@ -16,12 +16,10 @@ #ifndef ABSL_FLAGS_INTERNAL_COMMANDLINEFLAG_H_ #define ABSL_FLAGS_INTERNAL_COMMANDLINEFLAG_H_ -#include #include #include "absl/base/macros.h" #include "absl/flags/marshalling.h" -#include "absl/synchronization/mutex.h" #include "absl/types/optional.h" namespace absl { @@ -151,14 +149,6 @@ inline size_t Sizeof(FlagOpFn op) { 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 inline bool IsOfType() const { - return op_ == &flags_internal::FlagOps; + return TypeId() == &flags_internal::FlagOps; } // Attempts to retrieve the flag value. Returns value on success, // absl::nullopt otherwise. template absl::optional Get() const { - if (IsRetired() || flags_internal::FlagOps != op_) return absl::nullopt; - - T res; - Read(&res, flags_internal::FlagOps); + if (IsRetired() || !IsOfType()) { + return absl::nullopt; + } - return res; + // Implementation notes: + // + // We are wrapping a union around the value of `T` to serve three purposes: + // + // 1. `U.value` has correct size and alignment for a value of type `T` + // 2. The `U.value` constructor is not invoked since U's constructor does + // not + // do it explicitly. + // 3. The `U.value` destructor is invoked since U's destructor does it + // explicitly. This makes `U` a kind of RAII wrapper around non default + // constructible value of T, which is destructed when we leave the + // scope. We do need to destroy U.value, which is constructed by + // CommandLineFlag::Read even though we left it in a moved-from state + // after std::move. + // + // All of this serves to avoid requiring `T` being default constructible. + union U { + T value; + U() {} + ~U() { value.~T(); } + }; + U u; + + Read(&u.value); + return std::move(u.value); } + // Polymorphic access methods + + // Returns true iff this object corresponds to retired flag + virtual bool IsRetired() const { return false; } + // Returns true iff this is a handle to an Abseil Flag. + virtual bool IsAbseilFlag() const { return true; } + // Returns id of the flag's value type. + virtual flags_internal::FlagOpFn TypeId() const = 0; + virtual bool IsModified() const = 0; + virtual bool IsSpecifiedOnCommandLine() const = 0; + virtual std::string DefaultValue() const = 0; + virtual std::string CurrentValue() const = 0; + + // Interfaces to operate on validators. + virtual bool ValidateInputValue(absl::string_view value) const = 0; + // Interface to save flag to some persistent state. Returns current flag state // or nullptr if flag does not support saving and restoring a state. virtual std::unique_ptr SaveState() = 0; - // Interfaces to overate on callbacks. - virtual 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 @@ -289,74 +276,28 @@ class CommandLineFlag { // * Update the flag's default value // * Update the current flag value if it was never set before // The mode is selected based on `set_mode` parameter. - bool SetFromString(absl::string_view value, - flags_internal::FlagSettingMode set_mode, - flags_internal::ValueSource source, std::string* error); + virtual bool SetFromString(absl::string_view value, + flags_internal::FlagSettingMode set_mode, + flags_internal::ValueSource source, + std::string* error) = 0; - void CheckDefaultValueParsingRoundtrip() const; + // Checks that flags default value can be converted to std::string and back to the + // flag's value type. + virtual void CheckDefaultValueParsingRoundtrip() const = 0; // Constant configuration for a particular flag. protected: ~CommandLineFlag() = default; - // Thread safe access to mutation counter. - int64_t MutationCounter() const; - - const char* const name_; - const HelpText help_; - const char* const filename_; - - const FlagOpFn op_; // Type-specific handler - const FlagMarshallingOpFn marshalling_op_; // Marshalling ops handler - const InitialValGenFunc make_init_value_; // Makes initial value for the flag - std::atomic inited_; // fields have been lazily initialized - - // Mutable state (guarded by locks_->primary_mu). - bool modified_; // Has flag value been modified? - bool on_command_line_; // Specified on command line. - void* def_; // Lazily initialized pointer to default value - void* cur_; // Lazily initialized pointer to current value - int64_t counter_; // Mutation counter - - // 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 ABSL_LOCK_RETURNED(locks_->primary_mu); - - // 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 bool TryParseLocked(CommandLineFlag* flag, void* dst, - absl::string_view value, std::string* err); - friend absl::Mutex* InitFlag(CommandLineFlag* flag); -}; + const char* const name_; // Flags name passed to ABSL_FLAG as second arg. + const HelpText help_; // The function generating help message. + const char* const filename_; // The file name where ABSL_FLAG resides. -// 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); + private: + // Copy-construct a new value of the flag's type in a memory referenced by + // the dst based on the current flag's value. + virtual void Read(void* dst) const = 0; +}; // 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, diff --git a/absl/flags/internal/flag.cc b/absl/flags/internal/flag.cc index 0f4035819cd..061113d7655 100644 --- a/absl/flags/internal/flag.cc +++ b/absl/flags/internal/flag.cc @@ -15,36 +15,331 @@ #include "absl/flags/internal/flag.h" +#include "absl/base/optimization.h" #include "absl/synchronization/mutex.h" namespace absl { namespace flags_internal { +namespace { -// 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 InvokeCallback(absl::Mutex* primary_mu, absl::Mutex* callback_mu, - FlagCallback cb) ABSL_EXCLUSIVE_LOCKS_REQUIRED(primary_mu) { - if (!cb) return; - - // When executing the callback we need the primary flag's mutex to be - // unlocked so that callback can retrieve the flag's value. - primary_mu->Unlock(); +// Currently we only validate flag values for user-defined flag types. +bool ShouldValidateFlagValue(const CommandLineFlag& flag) { +#define DONT_VALIDATE(T) \ + if (flag.IsOfType()) return false; + ABSL_FLAGS_INTERNAL_FOR_EACH_LOCK_FREE(DONT_VALIDATE) + DONT_VALIDATE(std::string) + DONT_VALIDATE(std::vector) +#undef DONT_VALIDATE + return true; +} + +} // namespace + +void FlagImpl::Init() { + ABSL_CONST_INIT static absl::Mutex init_lock(absl::kConstInit); + + { + absl::MutexLock lock(&init_lock); + + if (locks_ == nullptr) { // Must initialize Mutexes for this flag. + locks_ = new FlagImpl::CommandLineFlagLocks; + } + } + + absl::MutexLock lock(&locks_->primary_mu); + + if (def_ != nullptr) { + inited_.store(true, std::memory_order_release); + } else { + // Need to initialize def and cur fields. + def_ = (*initial_value_gen_)(); + cur_ = Clone(op_, def_); + StoreAtomic(); + inited_.store(true, std::memory_order_release); + InvokeCallback(); + } +} + +// Ensures that the lazily initialized data is initialized, +// and returns pointer to the mutex guarding flags data. +absl::Mutex* FlagImpl::DataGuard() const + ABSL_LOCK_RETURNED(locks_->primary_mu) { + if (ABSL_PREDICT_FALSE(!inited_.load(std::memory_order_acquire))) { + const_cast(this)->Init(); + } + + // All fields initialized; locks_ is therefore safe to read. + return &locks_->primary_mu; +} + +void FlagImpl::Destroy() const { { - absl::MutexLock lock(callback_mu); - cb(); + absl::MutexLock l(DataGuard()); + + // Values are heap allocated for Abseil Flags. + if (cur_) Delete(op_, cur_); + if (def_) Delete(op_, def_); } - primary_mu->Lock(); + delete locks_; +} + +bool FlagImpl::IsModified() const { + absl::MutexLock l(DataGuard()); + return modified_; +} + +bool FlagImpl::IsSpecifiedOnCommandLine() const { + absl::MutexLock l(DataGuard()); + return on_command_line_; +} + +std::string FlagImpl::DefaultValue() const { + absl::MutexLock l(DataGuard()); + + return Unparse(marshalling_op_, def_); +} + +std::string FlagImpl::CurrentValue() const { + absl::MutexLock l(DataGuard()); + + return Unparse(marshalling_op_, cur_); +} + +void FlagImpl::SetCallback( + const flags_internal::FlagCallback mutation_callback) { + absl::MutexLock l(DataGuard()); + + callback_ = mutation_callback; + + InvokeCallback(); +} + +void FlagImpl::InvokeCallback() const + ABSL_EXCLUSIVE_LOCKS_REQUIRED(locks_->primary_mu) { + if (!callback_) return; + + // 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. + DataGuard()->Unlock(); + + { + absl::MutexLock lock(&locks_->callback_mu); + callback_(); + } + + DataGuard()->Lock(); +} + +bool FlagImpl::RestoreState(const CommandLineFlag& flag, const void* value, + bool modified, bool on_command_line, + int64_t counter) { + { + absl::MutexLock l(DataGuard()); + + if (counter_ == counter) return false; + } + + Write(flag, value, op_); + + { + absl::MutexLock l(DataGuard()); + + modified_ = modified; + on_command_line_ = on_command_line; + } + + return true; +} + +// Attempts to parse supplied `value` string using parsing routine in the `flag` +// argument. If parsing successful, this function 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 FlagImpl::TryParse(const CommandLineFlag& flag, void* dst, + absl::string_view value, std::string* err) const + ABSL_EXCLUSIVE_LOCKS_REQUIRED(locks_->primary_mu) { + void* tentative_value = Clone(op_, def_); + std::string parse_err; + if (!Parse(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(op_, tentative_value); + return false; + } + + Copy(op_, tentative_value, dst); + Delete(op_, tentative_value); + return true; +} + +void FlagImpl::Read(const CommandLineFlag& flag, void* dst, + const flags_internal::FlagOpFn dst_op) const { + absl::ReaderMutexLock l(DataGuard()); + + // `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 '", flag.Name(), + "' is defined as one type and declared as another")); + } + CopyConstruct(op_, cur_, dst); +} + +void FlagImpl::StoreAtomic() ABSL_EXCLUSIVE_LOCKS_REQUIRED(locks_->primary_mu) { + size_t data_size = Sizeof(op_); + + if (data_size <= sizeof(int64_t)) { + int64_t t = 0; + std::memcpy(&t, cur_, data_size); + atomic_.store(t, std::memory_order_release); + } +} + +void FlagImpl::Write(const CommandLineFlag& flag, const void* src, + const flags_internal::FlagOpFn src_op) { + absl::MutexLock l(DataGuard()); + + // `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 '", flag.Name(), + "' is defined as one type and declared as another")); + } + + if (ShouldValidateFlagValue(flag)) { + 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)) { + ABSL_INTERNAL_LOG(ERROR, + absl::StrCat("Attempt to set flag '", flag.Name(), + "' to invalid value ", src_as_str)); + } + Delete(op_, obj); + } + + modified_ = true; + counter_++; + Copy(op_, src, cur_); + + StoreAtomic(); + InvokeCallback(); +} + +// Sets the value of the flag based on specified string `value`. If the flag +// was successfully set to new value, it returns true. Otherwise, sets `err` +// to indicate the error, leaves the flag unchanged, and returns false. There +// are three ways to set the flag's value: +// * Update the current flag value +// * Update the flag's default value +// * Update the current flag value if it was never set before +// The mode is selected based on 'set_mode' parameter. +bool FlagImpl::SetFromString(const CommandLineFlag& flag, + absl::string_view value, FlagSettingMode set_mode, + ValueSource source, std::string* err) { + absl::MutexLock l(DataGuard()); + + switch (set_mode) { + case SET_FLAGS_VALUE: { + // set or modify the flag's value + if (!TryParse(flag, cur_, value, err)) return false; + modified_ = true; + counter_++; + StoreAtomic(); + InvokeCallback(); + + if (source == kCommandLine) { + on_command_line_ = true; + } + break; + } + case SET_FLAG_IF_DEFAULT: { + // set the flag's value, but only if it hasn't been set by someone else + if (!modified_) { + if (!TryParse(flag, cur_, value, err)) return false; + modified_ = true; + counter_++; + StoreAtomic(); + 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(Name(), " is already set to ", + // CurrentValue(), "\n"); + // return false; + return true; + } + break; + } + case SET_FLAGS_DEFAULT: { + // modify the flag's default-value + if (!TryParse(flag, def_, value, err)) return false; + + if (!modified_) { + // Need to set both default value *and* current, in this case + Copy(op_, def_, cur_); + StoreAtomic(); + InvokeCallback(); + } + break; + } + } + + return true; +} + +void FlagImpl::CheckDefaultValueParsingRoundtrip( + const CommandLineFlag& flag) const { + std::string v = DefaultValue(); + + absl::MutexLock lock(DataGuard()); + + void* dst = Clone(op_, def_); + std::string error; + if (!flags_internal::Parse(marshalling_op_, v, dst, &error)) { + ABSL_INTERNAL_LOG( + FATAL, + absl::StrCat("Flag ", flag.Name(), " (from ", flag.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(op_, dst); +} + +bool FlagImpl::ValidateInputValue(absl::string_view value) const { + absl::MutexLock l(DataGuard()); + + void* obj = Clone(op_, def_); + std::string ignored_error; + const bool result = + flags_internal::Parse(marshalling_op_, value, obj, &ignored_error); + Delete(op_, obj); + return result; } } // namespace flags_internal diff --git a/absl/flags/internal/flag.h b/absl/flags/internal/flag.h index 2b21c440736..ce0ccf2d31d 100644 --- a/absl/flags/internal/flag.h +++ b/absl/flags/internal/flag.h @@ -16,12 +16,15 @@ #ifndef ABSL_FLAGS_INTERNAL_FLAG_H_ #define ABSL_FLAGS_INTERNAL_FLAG_H_ +#include #include +#include "absl/base/thread_annotations.h" #include "absl/flags/internal/commandlineflag.h" #include "absl/flags/internal/registry.h" #include "absl/memory/memory.h" #include "absl/strings/str_cat.h" +#include "absl/synchronization/mutex.h" namespace absl { namespace flags_internal { @@ -66,6 +69,124 @@ using FlagCallback = void (*)(); void InvokeCallback(absl::Mutex* primary_mu, absl::Mutex* callback_mu, FlagCallback cb) ABSL_EXCLUSIVE_LOCKS_REQUIRED(primary_mu); +// The class encapsulates the Flag's data and safe access to it. +class FlagImpl { + public: + constexpr FlagImpl(const flags_internal::FlagOpFn op, + const flags_internal::FlagMarshallingOpFn marshalling_op, + const flags_internal::InitialValGenFunc initial_value_gen) + : op_(op), + marshalling_op_(marshalling_op), + initial_value_gen_(initial_value_gen) {} + + // Forces destruction of the Flag's data. + void Destroy() const; + + // Constant access methods + bool IsModified() const ABSL_LOCKS_EXCLUDED(locks_->primary_mu); + bool IsSpecifiedOnCommandLine() const ABSL_LOCKS_EXCLUDED(locks_->primary_mu); + std::string DefaultValue() const ABSL_LOCKS_EXCLUDED(locks_->primary_mu); + std::string CurrentValue() const ABSL_LOCKS_EXCLUDED(locks_->primary_mu); + void Read(const CommandLineFlag& flag, void* dst, + const flags_internal::FlagOpFn dst_op) const + ABSL_LOCKS_EXCLUDED(locks_->primary_mu); + // Attempts to parse supplied `value` std::string. + bool TryParse(const CommandLineFlag& flag, void* dst, absl::string_view value, + std::string* err) const + ABSL_EXCLUSIVE_LOCKS_REQUIRED(locks_->primary_mu); + template + bool AtomicGet(T* v) const { + const int64_t r = atomic_.load(std::memory_order_acquire); + if (r != flags_internal::AtomicInit()) { + std::memcpy(v, &r, sizeof(T)); + return true; + } + + return false; + } + + // Mutating access methods + void Write(const CommandLineFlag& flag, const void* src, + const flags_internal::FlagOpFn src_op) + ABSL_LOCKS_EXCLUDED(locks_->primary_mu); + bool SetFromString(const CommandLineFlag& flag, absl::string_view value, + FlagSettingMode set_mode, ValueSource source, + std::string* err) ABSL_LOCKS_EXCLUDED(locks_->primary_mu); + // If possible, updates copy of the Flag's value that is stored in an + // atomic word. + void StoreAtomic() ABSL_EXCLUSIVE_LOCKS_REQUIRED(locks_->primary_mu); + + // Interfaces to operate on callbacks. + void SetCallback(const flags_internal::FlagCallback mutation_callback) + ABSL_LOCKS_EXCLUDED(locks_->primary_mu); + void InvokeCallback() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(locks_->primary_mu); + + // Interfaces to save/restore mutable flag data + template + std::unique_ptr SaveState( + Flag* flag) const ABSL_LOCKS_EXCLUDED(locks_->primary_mu) { + T&& cur_value = flag->Get(); + absl::MutexLock l(DataGuard()); + + return absl::make_unique>( + flag, std::move(cur_value), modified_, on_command_line_, counter_); + } + bool RestoreState(const CommandLineFlag& flag, const void* value, + bool modified, bool on_command_line, int64_t counter) + ABSL_LOCKS_EXCLUDED(locks_->primary_mu); + + // Value validation interfaces. + void CheckDefaultValueParsingRoundtrip(const CommandLineFlag& flag) const + ABSL_LOCKS_EXCLUDED(locks_->primary_mu); + bool ValidateInputValue(absl::string_view value) const + ABSL_LOCKS_EXCLUDED(locks_->primary_mu); + + private: + // Lazy initialization of the Flag's data. + void Init(); + // Ensures that the lazily initialized data is initialized, + // and returns pointer to the mutex guarding flags data. + absl::Mutex* DataGuard() const ABSL_LOCK_RETURNED(locks_->primary_mu); + + // Immutable Flag's data. + const FlagOpFn op_; // Type-specific handler + const FlagMarshallingOpFn marshalling_op_; // Marshalling ops handler + const InitialValGenFunc initial_value_gen_; // Makes flag's initial value + + // Mutable Flag's data. (guarded by locks_->primary_mu). + // Indicates that locks_, cur_ and def_ fields have been lazily initialized. + std::atomic inited_{false}; + // Has flag value been modified? + bool modified_ ABSL_GUARDED_BY(locks_->primary_mu) = false; + // Specified on command line. + bool on_command_line_ ABSL_GUARDED_BY(locks_->primary_mu) = false; + // Lazily initialized pointer to default value + void* def_ ABSL_GUARDED_BY(locks_->primary_mu) = nullptr; + // Lazily initialized pointer to current value + void* cur_ ABSL_GUARDED_BY(locks_->primary_mu) = nullptr; + // Mutation counter + int64_t counter_ ABSL_GUARDED_BY(locks_->primary_mu) = 0; + // For some types, a copy of the current value is kept in an atomically + // accessible field. + std::atomic atomic_{flags_internal::AtomicInit()}; + // Mutation callback + FlagCallback callback_ = nullptr; + + // 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 + // 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 + }; + + CommandLineFlagLocks* locks_ = nullptr; // locks, laziliy allocated. +}; + // This is "unspecified" implementation of absl::Flag type. template class Flag final : public flags_internal::CommandLineFlag { @@ -76,30 +197,11 @@ class Flag final : public flags_internal::CommandLineFlag { const flags_internal::InitialValGenFunc initial_value_gen) : flags_internal::CommandLineFlag( name, flags_internal::HelpText::FromFunctionPointer(help_gen), - filename, &flags_internal::FlagOps, marshalling_op, - initial_value_gen, - /*def=*/nullptr, - /*cur=*/nullptr), - atomic_(flags_internal::AtomicInit()), - callback_(nullptr) {} + filename), + impl_(&flags_internal::FlagOps, marshalling_op, initial_value_gen) {} T Get() const { - // Implementation notes: - // - // We are wrapping a union around the value of `T` to serve three purposes: - // - // 1. `U.value` has correct size and alignment for a value of type `T` - // 2. The `U.value` constructor is not invoked since U's constructor does - // not - // do it explicitly. - // 3. The `U.value` destructor is invoked since U's destructor does it - // explicitly. This makes `U` a kind of RAII wrapper around non default - // constructible value of T, which is destructed when we leave the - // scope. We do need to destroy U.value, which is constructed by - // CommandLineFlag::Read even though we left it in a moved-from state - // after std::move. - // - // All of this serves to avoid requiring `T` being default constructible. + // See implementation notes in CommandLineFlag::Get(). union U { T value; U() {} @@ -107,89 +209,70 @@ class Flag final : public flags_internal::CommandLineFlag { }; U u; - Read(&u.value, &flags_internal::FlagOps); + impl_.Read(*this, &u.value, &flags_internal::FlagOps); return std::move(u.value); } - bool AtomicGet(T* v) const { - const int64_t r = atomic_.load(std::memory_order_acquire); - if (r != flags_internal::AtomicInit()) { - std::memcpy(v, &r, sizeof(T)); - return true; - } - - return false; - } + bool AtomicGet(T* v) const { return impl_.AtomicGet(v); } - void Set(const T& v) { Write(&v, &flags_internal::FlagOps); } + void Set(const T& v) { impl_.Write(*this, &v, &flags_internal::FlagOps); } void SetCallback(const flags_internal::FlagCallback mutation_callback) { - absl::MutexLock l(InitFlagIfNecessary()); - - callback_ = mutation_callback; - - InvokeCallback(); + impl_.SetCallback(mutation_callback); } - private: - friend class FlagState; - - void Destroy() const override { - // Values are heap allocated for Abseil Flags. - if (cur_) Delete(op_, cur_); - if (def_) Delete(op_, def_); - - delete locks_; + // CommandLineFlag interface + bool IsModified() const override { return impl_.IsModified(); } + bool IsSpecifiedOnCommandLine() const override { + return impl_.IsSpecifiedOnCommandLine(); } + std::string DefaultValue() const override { return impl_.DefaultValue(); } + std::string CurrentValue() const override { return impl_.CurrentValue(); } - void StoreAtomic() override { - if (sizeof(T) <= sizeof(int64_t)) { - int64_t t = 0; - std::memcpy(&t, cur_, (std::min)(sizeof(T), sizeof(int64_t))); - atomic_.store(t, std::memory_order_release); - } + bool ValidateInputValue(absl::string_view value) const override { + return impl_.ValidateInputValue(value); } // Interfaces to save and restore flags to/from persistent state. // Returns current flag state or nullptr if flag does not support // saving and restoring a state. std::unique_ptr SaveState() override { - T curr_value = Get(); - - absl::MutexLock l(InitFlagIfNecessary()); - - return absl::make_unique>( - this, std::move(curr_value), modified_, on_command_line_, counter_); + return impl_.SaveState(this); } // Restores the flag state to the supplied state object. If there is // nothing to restore returns false. Otherwise returns true. bool RestoreState(const flags_internal::FlagState& flag_state) { - if (MutationCounter() == flag_state.counter_) return false; - - Set(flag_state.cur_value_); + return impl_.RestoreState(*this, &flag_state.cur_value_, + flag_state.modified_, flag_state.on_command_line_, + flag_state.counter_); + } - // Race condition here? This should disappear once we move the rest of the - // flag's data into Flag's internals. + bool SetFromString(absl::string_view value, + flags_internal::FlagSettingMode set_mode, + flags_internal::ValueSource source, + std::string* error) override { + return impl_.SetFromString(*this, value, set_mode, source, error); + } - absl::MutexLock l(InitFlagIfNecessary()); - modified_ = flag_state.modified_; - on_command_line_ = flag_state.on_command_line_; - return true; + void CheckDefaultValueParsingRoundtrip() const override { + impl_.CheckDefaultValueParsingRoundtrip(*this); } - // Interfaces to overate on callbacks. - void InvokeCallback() override - ABSL_EXCLUSIVE_LOCKS_REQUIRED(locks_->primary_mu) { - flags_internal::InvokeCallback(&locks_->primary_mu, &locks_->callback_mu, - callback_); + private: + friend class FlagState; + + void Destroy() const override { impl_.Destroy(); } + + void Read(void* dst) const override { + impl_.Read(*this, dst, &flags_internal::FlagOps); + } + flags_internal::FlagOpFn TypeId() const override { + return &flags_internal::FlagOps; } // Flag's data - // For some types, a copy of the current value is kept in an atomically - // accessible field. - std::atomic atomic_; - FlagCallback callback_; // Mutation callback + FlagImpl impl_; }; template diff --git a/absl/flags/internal/registry.cc b/absl/flags/internal/registry.cc index 6b2564d13a7..ae7671a967e 100644 --- a/absl/flags/internal/registry.cc +++ b/absl/flags/internal/registry.cc @@ -118,7 +118,7 @@ void FlagRegistry::RegisterFlag(CommandLineFlag* flag) { (flag->IsRetired() ? old_flag->Filename() : flag->Filename()), "'."), true); - } else if (flag->op_ != old_flag->op_) { + } else if (flag->TypeId() != old_flag->TypeId()) { flags_internal::ReportUsageError( absl::StrCat("Flag '", flag->Name(), "' was defined more than once but with " @@ -275,38 +275,49 @@ namespace { class RetiredFlagObj final : public flags_internal::CommandLineFlag { public: - constexpr RetiredFlagObj(const char* name, FlagOpFn ops, - FlagMarshallingOpFn marshalling_ops) + constexpr RetiredFlagObj(const char* name, FlagOpFn ops) : flags_internal::CommandLineFlag( name, flags_internal::HelpText::FromStaticCString(nullptr), - /*filename=*/"RETIRED", ops, marshalling_ops, - /*initial_value_gen=*/nullptr, - /*def=*/nullptr, - /*cur=*/nullptr) {} + /*filename=*/"RETIRED"), + op_(ops) {} private: - bool IsRetired() const override { return true; } - void Destroy() const override { // Values are heap allocated for Retired Flags. - if (cur_) Delete(op_, cur_); - if (def_) Delete(op_, def_); - - if (locks_) delete locks_; - delete this; } + flags_internal::FlagOpFn TypeId() const override { return op_; } + bool IsRetired() const override { return true; } + bool IsModified() const override { return false; } + bool IsSpecifiedOnCommandLine() const override { return false; } + std::string DefaultValue() const override { return ""; } + std::string CurrentValue() const override { return ""; } + + // Any input is valid + bool ValidateInputValue(absl::string_view) const override { return true; } + std::unique_ptr SaveState() override { return nullptr; } + + bool SetFromString(absl::string_view, flags_internal::FlagSettingMode, + flags_internal::ValueSource, std::string*) override { + return false; + } + + void CheckDefaultValueParsingRoundtrip() const override {} + + void Read(void*) const override {} + + // Data members + const FlagOpFn op_; }; } // namespace -bool Retire(const char* name, FlagOpFn ops, - FlagMarshallingOpFn marshalling_ops) { - auto* flag = new flags_internal::RetiredFlagObj(name, ops, marshalling_ops); +bool Retire(const char* name, FlagOpFn ops) { + auto* flag = new flags_internal::RetiredFlagObj(name, ops); FlagRegistry::GlobalRegistry()->RegisterFlag(flag); return true; } diff --git a/absl/flags/internal/registry.h b/absl/flags/internal/registry.h index eb134a9f81e..1889f3a076d 100644 --- a/absl/flags/internal/registry.h +++ b/absl/flags/internal/registry.h @@ -76,14 +76,12 @@ bool RegisterCommandLineFlag(CommandLineFlag*); // // Retire flag with name "name" and type indicated by ops. -bool Retire(const char* name, FlagOpFn ops, - FlagMarshallingOpFn marshalling_ops); +bool Retire(const char* name, FlagOpFn ops); // Registered a retired flag with name 'flag_name' and type 'T'. template inline bool RetiredFlag(const char* flag_name) { - return flags_internal::Retire(flag_name, flags_internal::FlagOps, - flags_internal::FlagMarshallingOps); + return flags_internal::Retire(flag_name, flags_internal::FlagOps); } // If the flag is retired, returns true and indicates in |*type_is_bool| diff --git a/absl/random/internal/iostream_state_saver_test.cc b/absl/random/internal/iostream_state_saver_test.cc index 722766d0ff6..7bb8ad959cf 100644 --- a/absl/random/internal/iostream_state_saver_test.cc +++ b/absl/random/internal/iostream_state_saver_test.cc @@ -196,8 +196,8 @@ TEST(IOStreamStateSaver, RoundTripFloats) { EXPECT_EQ(-d, StreamRoundTrip(-d)); // Avoid undefined behavior (overflow/underflow). - if (d <= std::numeric_limits::max() && - d >= std::numeric_limits::lowest()) { + if (f <= static_cast(std::numeric_limits::max()) && + f >= static_cast(std::numeric_limits::lowest())) { int64_t x = static_cast(f); EXPECT_EQ(x, StreamRoundTrip(x)); } @@ -264,8 +264,8 @@ TEST(IOStreamStateSaver, RoundTripDoubles) { } // Avoid undefined behavior (overflow/underflow). - if (d <= std::numeric_limits::max() && - d >= std::numeric_limits::lowest()) { + if (d <= static_cast(std::numeric_limits::max()) && + d >= static_cast(std::numeric_limits::lowest())) { int64_t x = static_cast(d); EXPECT_EQ(x, StreamRoundTrip(x)); } diff --git a/absl/strings/internal/resize_uninitialized.h b/absl/strings/internal/resize_uninitialized.h index 469962b2749..0f5e964d62b 100644 --- a/absl/strings/internal/resize_uninitialized.h +++ b/absl/strings/internal/resize_uninitialized.h @@ -35,8 +35,7 @@ struct ResizeUninitializedTraits { static void Resize(string_type* s, size_t new_size) { s->resize(new_size); } }; -// __resize_default_init is provided by libc++ >= 8.0 and by Google's internal -// ::string implementation. +// __resize_default_init is provided by libc++ >= 8.0 template struct ResizeUninitializedTraits< string_type, absl::void_t() diff --git a/ci/cmake_install_test.sh b/ci/cmake_install_test.sh index 03eb04329ed..6a3c19bddbd 100755 --- a/ci/cmake_install_test.sh +++ b/ci/cmake_install_test.sh @@ -28,6 +28,5 @@ time docker run \ --rm \ -e CFLAGS="-Werror" \ -e CXXFLAGS="-Werror" \ - gcr.io/google.com/absl-177019/linux_gcc-latest:20190703 \ + gcr.io/google.com/absl-177019/linux_gcc-latest:20191018 \ /bin/bash CMake/install_test_project/test.sh $@ - diff --git a/ci/linux_clang-latest_libcxx_asan_bazel.sh b/ci/linux_clang-latest_libcxx_asan_bazel.sh index cc8c695aabd..fa38ff20f7b 100755 --- a/ci/linux_clang-latest_libcxx_asan_bazel.sh +++ b/ci/linux_clang-latest_libcxx_asan_bazel.sh @@ -36,7 +36,7 @@ if [ -z ${EXCEPTIONS_MODE:-} ]; then EXCEPTIONS_MODE="-fno-exceptions -fexceptions" fi -readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/linux_clang-latest:20190813" +readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/linux_clang-latest:20191018" # USE_BAZEL_CACHE=1 only works on Kokoro. # Without access to the credentials this won't work. diff --git a/ci/linux_clang-latest_libcxx_bazel.sh b/ci/linux_clang-latest_libcxx_bazel.sh index 5849a84eda8..c3319a1b8e8 100755 --- a/ci/linux_clang-latest_libcxx_bazel.sh +++ b/ci/linux_clang-latest_libcxx_bazel.sh @@ -36,7 +36,7 @@ if [ -z ${EXCEPTIONS_MODE:-} ]; then EXCEPTIONS_MODE="-fno-exceptions -fexceptions" fi -readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/linux_clang-latest:20190813" +readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/linux_clang-latest:20191018" # USE_BAZEL_CACHE=1 only works on Kokoro. # Without access to the credentials this won't work. diff --git a/ci/linux_clang-latest_libcxx_tsan_bazel.sh b/ci/linux_clang-latest_libcxx_tsan_bazel.sh index 8721ada2ed7..200541b9e9c 100755 --- a/ci/linux_clang-latest_libcxx_tsan_bazel.sh +++ b/ci/linux_clang-latest_libcxx_tsan_bazel.sh @@ -36,7 +36,7 @@ if [ -z ${EXCEPTIONS_MODE:-} ]; then EXCEPTIONS_MODE="-fno-exceptions -fexceptions" fi -readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/linux_clang-latest:20190813" +readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/linux_clang-latest:20191018" # USE_BAZEL_CACHE=1 only works on Kokoro. # Without access to the credentials this won't work. diff --git a/ci/linux_clang-latest_libstdcxx_bazel.sh b/ci/linux_clang-latest_libstdcxx_bazel.sh index 78ce0b32e49..5ea18373570 100755 --- a/ci/linux_clang-latest_libstdcxx_bazel.sh +++ b/ci/linux_clang-latest_libstdcxx_bazel.sh @@ -36,7 +36,7 @@ if [ -z ${EXCEPTIONS_MODE:-} ]; then EXCEPTIONS_MODE="-fno-exceptions -fexceptions" fi -readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/linux_clang-latest:20190813" +readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/linux_clang-latest:20191018" # USE_BAZEL_CACHE=1 only works on Kokoro. # Without access to the credentials this won't work. diff --git a/ci/linux_gcc-4.9_libstdcxx_bazel.sh b/ci/linux_gcc-4.9_libstdcxx_bazel.sh index c3b936bc209..f8102cc0a1b 100755 --- a/ci/linux_gcc-4.9_libstdcxx_bazel.sh +++ b/ci/linux_gcc-4.9_libstdcxx_bazel.sh @@ -36,7 +36,7 @@ if [ -z ${EXCEPTIONS_MODE:-} ]; then EXCEPTIONS_MODE="-fno-exceptions -fexceptions" fi -readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/linux_gcc-4.9:20190702" +readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/linux_gcc-4.9:20191018" # USE_BAZEL_CACHE=1 only works on Kokoro. # Without access to the credentials this won't work. diff --git a/ci/linux_gcc-latest_libstdcxx_bazel.sh b/ci/linux_gcc-latest_libstdcxx_bazel.sh index 10f6be2135a..a6efa197d9d 100755 --- a/ci/linux_gcc-latest_libstdcxx_bazel.sh +++ b/ci/linux_gcc-latest_libstdcxx_bazel.sh @@ -36,7 +36,7 @@ if [ -z ${EXCEPTIONS_MODE:-} ]; then EXCEPTIONS_MODE="-fno-exceptions -fexceptions" fi -readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/linux_gcc-latest:20190703" +readonly DOCKER_CONTAINER="gcr.io/google.com/absl-177019/linux_gcc-latest:20191018" # USE_BAZEL_CACHE=1 only works on Kokoro. # Without access to the credentials this won't work. diff --git a/ci/linux_gcc-latest_libstdcxx_cmake.sh b/ci/linux_gcc-latest_libstdcxx_cmake.sh index 3e831c14b86..ec3287481c4 100755 --- a/ci/linux_gcc-latest_libstdcxx_cmake.sh +++ b/ci/linux_gcc-latest_libstdcxx_cmake.sh @@ -47,7 +47,7 @@ for std in ${ABSL_CMAKE_CXX_STANDARDS}; do --rm \ -e CFLAGS="-Werror" \ -e CXXFLAGS="-Werror" \ - gcr.io/google.com/absl-177019/linux_gcc-latest:20190703 \ + gcr.io/google.com/absl-177019/linux_gcc-latest:20191018 \ /bin/bash -c " cd /buildfs && \ cmake /abseil-cpp \ From ecc0033b54847f6c9ee37dbb0be8aa17e5b6d37b Mon Sep 17 00:00:00 2001 From: Loo Rong Jie Date: Tue, 22 Oct 2019 08:34:46 +0800 Subject: [PATCH 0008/3164] Always enable proper symbolize implementation on Windows (#257) --- absl/debugging/BUILD.bazel | 10 +++++++-- absl/debugging/CMakeLists.txt | 3 +++ absl/debugging/symbolize.cc | 9 +++----- absl/debugging/symbolize_test.cc | 35 +++++++++++++++++++++++++----- absl/debugging/symbolize_win32.inc | 5 ++--- 5 files changed, 45 insertions(+), 17 deletions(-) diff --git a/absl/debugging/BUILD.bazel b/absl/debugging/BUILD.bazel index b87c55a54f6..beec279ff8e 100644 --- a/absl/debugging/BUILD.bazel +++ b/absl/debugging/BUILD.bazel @@ -70,8 +70,14 @@ cc_library( cc_test( name = "symbolize_test", srcs = ["symbolize_test.cc"], - copts = ABSL_TEST_COPTS, - linkopts = ABSL_DEFAULT_LINKOPTS, + copts = ABSL_TEST_COPTS + select({ + "//absl:windows": ["/Z7"], + "//conditions:default": [], + }), + linkopts = ABSL_DEFAULT_LINKOPTS + select({ + "//absl:windows": ["/DEBUG"], + "//conditions:default": [], + }), deps = [ ":stack_consumption", ":symbolize", diff --git a/absl/debugging/CMakeLists.txt b/absl/debugging/CMakeLists.txt index 81492c0c0f3..b3e35b6d744 100644 --- a/absl/debugging/CMakeLists.txt +++ b/absl/debugging/CMakeLists.txt @@ -62,6 +62,9 @@ absl_cc_test( "symbolize_test.cc" COPTS ${ABSL_TEST_COPTS} + $<$:-Z7> + LINKOPTS + $<$:-DEBUG> DEPS absl::stack_consumption absl::symbolize diff --git a/absl/debugging/symbolize.cc b/absl/debugging/symbolize.cc index 24e3a7f0a0e..54ed97002a1 100644 --- a/absl/debugging/symbolize.cc +++ b/absl/debugging/symbolize.cc @@ -16,12 +16,9 @@ #if defined(ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE) #include "absl/debugging/symbolize_elf.inc" -#elif defined(_WIN32) && defined(_DEBUG) -// The Windows Symbolizer only works in debug mode. Note that _DEBUG -// is the macro that defines whether or not MS C-Runtime debug info is -// available. Note that the PDB files containing the debug info must -// also be available to the program at runtime for the symbolizer to -// work. +#elif defined(_WIN32) +// The Windows Symbolizer only works if PDB files containing the debug info +// are available to the program at runtime. #include "absl/debugging/symbolize_win32.inc" #else #include "absl/debugging/symbolize_unimplemented.inc" diff --git a/absl/debugging/symbolize_test.cc b/absl/debugging/symbolize_test.cc index 533799a6ebc..0bac60b9ea2 100644 --- a/absl/debugging/symbolize_test.cc +++ b/absl/debugging/symbolize_test.cc @@ -35,10 +35,29 @@ using testing::Contains; +#ifdef _WIN32 +#define ABSL_SYMBOLIZE_TEST_NOINLINE __declspec(noinline) +#else +#define ABSL_SYMBOLIZE_TEST_NOINLINE ABSL_ATTRIBUTE_NOINLINE +#endif + // Functions to symbolize. Use C linkage to avoid mangled names. extern "C" { -void nonstatic_func() { ABSL_BLOCK_TAIL_CALL_OPTIMIZATION(); } -static void static_func() { ABSL_BLOCK_TAIL_CALL_OPTIMIZATION(); } +ABSL_SYMBOLIZE_TEST_NOINLINE void nonstatic_func() { + // The next line makes this a unique function to prevent the compiler from + // folding identical functions together. + volatile int x = __LINE__; + static_cast(x); + ABSL_BLOCK_TAIL_CALL_OPTIMIZATION(); +} + +ABSL_SYMBOLIZE_TEST_NOINLINE static void static_func() { + // The next line makes this a unique function to prevent the compiler from + // folding identical functions together. + volatile int x = __LINE__; + static_cast(x); + ABSL_BLOCK_TAIL_CALL_OPTIMIZATION(); +} } // extern "C" struct Foo { @@ -46,7 +65,11 @@ struct Foo { }; // A C++ method that should have a mangled name. -void ABSL_ATTRIBUTE_NOINLINE Foo::func(int) { +ABSL_SYMBOLIZE_TEST_NOINLINE void Foo::func(int) { + // The next line makes this a unique function to prevent the compiler from + // folding identical functions together. + volatile int x = __LINE__; + static_cast(x); ABSL_BLOCK_TAIL_CALL_OPTIMIZATION(); } @@ -449,14 +472,14 @@ void ABSL_ATTRIBUTE_NOINLINE TestWithReturnAddress() { #endif } -#elif defined(_WIN32) && defined(_DEBUG) +#elif defined(_WIN32) TEST(Symbolize, Basics) { EXPECT_STREQ("nonstatic_func", TrySymbolize((void *)(&nonstatic_func))); // The name of an internal linkage symbol is not specified; allow either a // mangled or an unmangled name here. - const char* static_func_symbol = TrySymbolize((void *)(&static_func)); + const char *static_func_symbol = TrySymbolize((void *)(&static_func)); ASSERT_TRUE(static_func_symbol != nullptr); EXPECT_TRUE(strstr(static_func_symbol, "static_func") != nullptr); @@ -483,7 +506,7 @@ TEST(Symbolize, Truncation) { } TEST(Symbolize, SymbolizeWithDemangling) { - const char* result = TrySymbolize((void *)(&Foo::func)); + const char *result = TrySymbolize((void *)(&Foo::func)); ASSERT_TRUE(result != nullptr); EXPECT_TRUE(strstr(result, "Foo::func") != nullptr) << result; } diff --git a/absl/debugging/symbolize_win32.inc b/absl/debugging/symbolize_win32.inc index 5a55f29d296..0fb4b803769 100644 --- a/absl/debugging/symbolize_win32.inc +++ b/absl/debugging/symbolize_win32.inc @@ -57,9 +57,8 @@ bool Symbolize(const void *pc, char *out, int out_size) { if (out_size <= 0) { return false; } - std::aligned_storage::type buf; - SYMBOL_INFO *symbol = reinterpret_cast(&buf); + alignas(SYMBOL_INFO) char buf[sizeof(SYMBOL_INFO) + MAX_SYM_NAME]; + SYMBOL_INFO *symbol = reinterpret_cast(buf); symbol->SizeOfStruct = sizeof(SYMBOL_INFO); symbol->MaxNameLen = MAX_SYM_NAME; if (!SymFromAddr(process, reinterpret_cast(pc), nullptr, symbol)) { From 19b021cb3ff23048dfbe236a4e611925d8930831 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Tue, 22 Oct 2019 09:35:23 -0700 Subject: [PATCH 0009/3164] Export of internal Abseil changes -- 3f913305d6582ac0c66c25f71275d89232dc97bf by CJ Johnson : Internal change PiperOrigin-RevId: 276081163 -- 19c65f15e8b67e4677ebed9f815f934091368f41 by CJ Johnson : Internal change PiperOrigin-RevId: 275964906 -- a40557fe4575a6bc9bed4fd83ed8c748bc3f15ff by Derek Mauro : Always enable symbolization on Windows. This doesn't require debug builds, only PDB files. https://github.com/abseil/abseil-cpp/pull/257 PiperOrigin-RevId: 275961084 -- 953bec0e67d2495e901b940b67b6330291871998 by CJ Johnson : Test FixedArray exception safety for OOM using ThrowingAllocator PiperOrigin-RevId: 275921366 -- 13211ea447af1daf1701ccb1c7c6b2cbaa68eb93 by CJ Johnson : Internal change PiperOrigin-RevId: 275913105 -- 09dccef0c829cfab5847137ead6866c511e9ce51 by CJ Johnson : Internal change PiperOrigin-RevId: 275912911 -- 65db8fc06f50e0989aee2d264f8f7a348ff95c17 by Samuel Benzaquen : Move FormatPack to bind.cc. We do not get any benefit from inlining this function, but it creates unnecessary bloat at the call site. Moving it to the .cc file moves the initialization of the string out of the caller code. PiperOrigin-RevId: 275889073 GitOrigin-RevId: 3f913305d6582ac0c66c25f71275d89232dc97bf Change-Id: Ia471eebba7322a6ea4edc954e50b30a060a45e39 --- .../fixed_array_exception_safety_test.cc | 87 +++++++++++++++++-- .../inlined_vector_exception_safety_test.cc | 26 ++++-- absl/container/internal/inlined_vector.h | 8 +- absl/strings/internal/str_format/bind.cc | 9 ++ absl/strings/internal/str_format/bind.h | 8 +- 5 files changed, 115 insertions(+), 23 deletions(-) diff --git a/absl/container/fixed_array_exception_safety_test.cc b/absl/container/fixed_array_exception_safety_test.cc index 9aabfd5c95a..5ebeac05b7b 100644 --- a/absl/container/fixed_array_exception_safety_test.cc +++ b/absl/container/fixed_array_exception_safety_test.cc @@ -12,9 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "absl/container/fixed_array.h" - #include "absl/base/config.h" +#include "absl/container/fixed_array.h" #ifdef ABSL_HAVE_EXCEPTIONS @@ -37,10 +36,19 @@ constexpr int kUpdatedValue = 10; using ::testing::TestThrowingCtor; using Thrower = testing::ThrowingValue; +using ThrowAlloc = + testing::ThrowingAllocator; +using MoveThrower = testing::ThrowingValue; +using MoveThrowAlloc = + testing::ThrowingAllocator; + using FixedArr = absl::FixedArray; +using FixedArrWithAlloc = absl::FixedArray; -using MoveThrower = testing::ThrowingValue; using MoveFixedArr = absl::FixedArray; +using MoveFixedArrWithAlloc = + absl::FixedArray; TEST(FixedArrayExceptionSafety, CopyConstructor) { auto small = FixedArr(kSmallSize); @@ -50,6 +58,14 @@ TEST(FixedArrayExceptionSafety, CopyConstructor) { TestThrowingCtor(large); } +TEST(FixedArrayExceptionSafety, CopyConstructorWithAlloc) { + auto small = FixedArrWithAlloc(kSmallSize); + TestThrowingCtor(small); + + auto large = FixedArrWithAlloc(kLargeSize); + TestThrowingCtor(large); +} + TEST(FixedArrayExceptionSafety, MoveConstructor) { TestThrowingCtor(FixedArr(kSmallSize)); TestThrowingCtor(FixedArr(kLargeSize)); @@ -59,16 +75,35 @@ TEST(FixedArrayExceptionSafety, MoveConstructor) { TestThrowingCtor(MoveFixedArr(kLargeSize)); } +TEST(FixedArrayExceptionSafety, MoveConstructorWithAlloc) { + TestThrowingCtor(FixedArrWithAlloc(kSmallSize)); + TestThrowingCtor(FixedArrWithAlloc(kLargeSize)); + + // TypeSpec::kNoThrowMove + TestThrowingCtor(MoveFixedArrWithAlloc(kSmallSize)); + TestThrowingCtor(MoveFixedArrWithAlloc(kLargeSize)); +} + TEST(FixedArrayExceptionSafety, SizeConstructor) { TestThrowingCtor(kSmallSize); TestThrowingCtor(kLargeSize); } +TEST(FixedArrayExceptionSafety, SizeConstructorWithAlloc) { + TestThrowingCtor(kSmallSize); + TestThrowingCtor(kLargeSize); +} + TEST(FixedArrayExceptionSafety, SizeValueConstructor) { TestThrowingCtor(kSmallSize, Thrower()); TestThrowingCtor(kLargeSize, Thrower()); } +TEST(FixedArrayExceptionSafety, SizeValueConstructorWithAlloc) { + TestThrowingCtor(kSmallSize, Thrower()); + TestThrowingCtor(kLargeSize, Thrower()); +} + TEST(FixedArrayExceptionSafety, IteratorConstructor) { auto small = FixedArr(kSmallSize); TestThrowingCtor(small.begin(), small.end()); @@ -77,6 +112,14 @@ TEST(FixedArrayExceptionSafety, IteratorConstructor) { TestThrowingCtor(large.begin(), large.end()); } +TEST(FixedArrayExceptionSafety, IteratorConstructorWithAlloc) { + auto small = FixedArrWithAlloc(kSmallSize); + TestThrowingCtor(small.begin(), small.end()); + + auto large = FixedArrWithAlloc(kLargeSize); + TestThrowingCtor(large.begin(), large.end()); +} + TEST(FixedArrayExceptionSafety, InitListConstructor) { constexpr int small_inlined = 3; using SmallFixedArr = absl::FixedArray; @@ -90,7 +133,22 @@ TEST(FixedArrayExceptionSafety, InitListConstructor) { Thrower{}, Thrower{}, Thrower{}, Thrower{}, Thrower{}}); } -testing::AssertionResult ReadMemory(FixedArr* fixed_arr) { +TEST(FixedArrayExceptionSafety, InitListConstructorWithAlloc) { + constexpr int small_inlined = 3; + using SmallFixedArrWithAlloc = + absl::FixedArray; + + TestThrowingCtor(std::initializer_list{}); + // Test inlined allocation + TestThrowingCtor( + std::initializer_list{Thrower{}, Thrower{}}); + // Test out of line allocation + TestThrowingCtor(std::initializer_list{ + Thrower{}, Thrower{}, Thrower{}, Thrower{}, Thrower{}}); +} + +template +testing::AssertionResult ReadMemory(FixedArrT* fixed_arr) { // Marked volatile to prevent optimization. Used for running asan tests. volatile int sum = 0; for (const auto& thrower : *fixed_arr) { @@ -101,7 +159,7 @@ testing::AssertionResult ReadMemory(FixedArr* fixed_arr) { TEST(FixedArrayExceptionSafety, Fill) { auto test_fill = testing::MakeExceptionSafetyTester() - .WithContracts(ReadMemory) + .WithContracts(ReadMemory) .WithOperation([&](FixedArr* fixed_arr_ptr) { auto thrower = Thrower(kUpdatedValue, testing::nothrow_ctor); @@ -116,6 +174,25 @@ TEST(FixedArrayExceptionSafety, Fill) { .Test()); } +TEST(FixedArrayExceptionSafety, FillWithAlloc) { + auto test_fill = testing::MakeExceptionSafetyTester() + .WithContracts(ReadMemory) + .WithOperation([&](FixedArrWithAlloc* fixed_arr_ptr) { + auto thrower = + Thrower(kUpdatedValue, testing::nothrow_ctor); + fixed_arr_ptr->fill(thrower); + }); + + EXPECT_TRUE(test_fill + .WithInitialValue( + FixedArrWithAlloc(kSmallSize, Thrower(kInitialValue))) + .Test()); + EXPECT_TRUE(test_fill + .WithInitialValue( + FixedArrWithAlloc(kLargeSize, Thrower(kInitialValue))) + .Test()); +} + } // namespace } // namespace absl diff --git a/absl/container/inlined_vector_exception_safety_test.cc b/absl/container/inlined_vector_exception_safety_test.cc index 937e43a5bf2..0e6a05b5f6a 100644 --- a/absl/container/inlined_vector_exception_safety_test.cc +++ b/absl/container/inlined_vector_exception_safety_test.cc @@ -364,9 +364,11 @@ TYPED_TEST(OneSizeTest, EmplaceBack) { using VecT = typename TypeParam::VecT; constexpr static auto size = TypeParam::GetSizeAt(0); + // For testing calls to `emplace_back(...)` that reallocate. VecT full_vec{size}; full_vec.resize(full_vec.capacity()); + // For testing calls to `emplace_back(...)` that don't reallocate. VecT nonfull_vec{size}; nonfull_vec.reserve(size + 1); @@ -374,12 +376,11 @@ TYPED_TEST(OneSizeTest, EmplaceBack) { InlinedVectorInvariants); EXPECT_TRUE(tester.WithInitialValue(nonfull_vec).Test([](VecT* vec) { - vec->emplace_back(); // + vec->emplace_back(); })); - EXPECT_TRUE(tester.WithInitialValue(full_vec).Test([](VecT* vec) { - vec->emplace_back(); // - })); + EXPECT_TRUE(tester.WithInitialValue(full_vec).Test( + [](VecT* vec) { vec->emplace_back(); })); } TYPED_TEST(OneSizeTest, PopBack) { @@ -416,6 +417,19 @@ TYPED_TEST(OneSizeTest, Erase) { vec->erase(it); })); + EXPECT_TRUE(tester.Test([](VecT* vec) { + auto it = vec->begin(); + vec->erase(it, it); + })); + EXPECT_TRUE(tester.Test([](VecT* vec) { + auto it = vec->begin() + (vec->size() / 2); + vec->erase(it, it); + })); + EXPECT_TRUE(tester.Test([](VecT* vec) { + auto it = vec->begin() + (vec->size() - 1); + vec->erase(it, it); + })); + EXPECT_TRUE(tester.Test([](VecT* vec) { auto it = vec->begin(); vec->erase(it, it + 1); @@ -452,9 +466,7 @@ TYPED_TEST(TwoSizeTest, Reserve) { .WithInitialValue(VecT{from_size}) .WithContracts(InlinedVectorInvariants); - EXPECT_TRUE(tester.Test([](VecT* vec) { - vec->reserve(to_capacity); // - })); + EXPECT_TRUE(tester.Test([](VecT* vec) { vec->reserve(to_capacity); })); } TYPED_TEST(OneSizeTest, ShrinkToFit) { diff --git a/absl/container/internal/inlined_vector.h b/absl/container/internal/inlined_vector.h index 54369c85682..7d08f7e306e 100644 --- a/absl/container/internal/inlined_vector.h +++ b/absl/container/internal/inlined_vector.h @@ -539,12 +539,12 @@ template auto Storage::Resize(ValueAdapter values, size_type new_size) -> void { StorageView storage_view = MakeStorageView(); - AllocationTransaction allocation_tx(GetAllocPtr()); - ConstructionTransaction construction_tx(GetAllocPtr()); - IteratorValueAdapter move_values( MoveIterator(storage_view.data)); + AllocationTransaction allocation_tx(GetAllocPtr()); + ConstructionTransaction construction_tx(GetAllocPtr()); + absl::Span construct_loop; absl::Span move_construct_loop; absl::Span destroy_loop; @@ -727,8 +727,6 @@ auto Storage::EmplaceBack(Args&&... args) -> reference { template auto Storage::Erase(const_iterator from, const_iterator to) -> iterator { - assert(from != to); - StorageView storage_view = MakeStorageView(); size_type erase_size = std::distance(from, to); diff --git a/absl/strings/internal/str_format/bind.cc b/absl/strings/internal/str_format/bind.cc index a31859ece2b..45e335a3094 100644 --- a/absl/strings/internal/str_format/bind.cc +++ b/absl/strings/internal/str_format/bind.cc @@ -196,6 +196,15 @@ std::string& AppendPack(std::string* out, const UntypedFormatSpecImpl format, return *out; } +std::string FormatPack(const UntypedFormatSpecImpl format, + absl::Span args) { + std::string out; + if (ABSL_PREDICT_FALSE(!FormatUntyped(&out, format, args))) { + out.clear(); + } + return out; +} + int FprintF(std::FILE* output, const UntypedFormatSpecImpl format, absl::Span args) { FILERawSink sink(output); diff --git a/absl/strings/internal/str_format/bind.h b/absl/strings/internal/str_format/bind.h index 7df140a4363..7f46035113b 100644 --- a/absl/strings/internal/str_format/bind.h +++ b/absl/strings/internal/str_format/bind.h @@ -178,12 +178,8 @@ bool FormatUntyped(FormatRawSinkImpl raw_sink, std::string& AppendPack(std::string* out, UntypedFormatSpecImpl format, absl::Span args); -inline std::string FormatPack(const UntypedFormatSpecImpl format, - absl::Span args) { - std::string out; - AppendPack(&out, format, args); - return out; -} +std::string FormatPack(const UntypedFormatSpecImpl format, + absl::Span args); int FprintF(std::FILE* output, UntypedFormatSpecImpl format, absl::Span args); From 078b89b3c046d230ef3ad39494e5852184eb528b Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Wed, 23 Oct 2019 19:35:39 -0700 Subject: [PATCH 0010/3164] Export of internal Abseil changes -- e54b9c7bbb0c58475676c268e2e19c69f4bce48a by Jorg Brown : Tweak ABSL_PREDICT_TRUE slightly, for better code on some platforms and/or optimization levels. "false || (x)" is more verbose than "!!(x)", but ultimately more efficient. For example, given this code: void InitIfNecessary() { if (ABSL_PREDICT_TRUE(NeedsInit())) { SlowInitIfNecessary(); } } Clang with default optimization level will produce: Before this CL After this CL InitIfNecessary: InitIfNecessary: push rbp push rbp mov rbp, rsp mov rbp, rsp call NeedsInit call NeedsInit xor al, -1 xor al, -1 test al, 1 test al, 1 jne .LBB2_1 jne .LBB3_1 jmp .LBB2_2 jmp .LBB3_2 .LBB2_1: .LBB3_1: call SlowInitIfNecessary call SlowInitIfNecessary .LBB2_2: .LBB3_2: pop rbp pop rbp ret ret PiperOrigin-RevId: 276401386 -- 0a3c4dfd8342bf2b1b11a87f1c662c883f73cab7 by Abseil Team : Fix comment nit: sem_open => sem_init. The code calls sem_init, not sem_open, to initialize an unnamed semaphore. (sem_open creates or opens a named semaphore.) PiperOrigin-RevId: 276344072 -- b36a664e9459057509a90e83d3482e1d3a4c44c7 by Abseil Team : Fix typo in flat_hash_map.h: exchaged -> exchanged PiperOrigin-RevId: 276295792 -- 7bbd8d18276eb110c8335743e35fceb662ddf3d6 by Samuel Benzaquen : Add assertions to verify use of iterators. PiperOrigin-RevId: 276283300 -- 677398a8ffcb1f59182cffe57a4fe7ff147a0404 by Laramie Leavitt : Migrate distribution_impl.h/cc to generate_real.h/cc. Combine the methods RandU64To into a single method: GenerateRealFromBits(). Remove rejection sampling from absl::uniform_real_distribution. PiperOrigin-RevId: 276158675 -- c60c9d11d24b0c546329d998e78e15a84b3153f5 by Abseil Team : Internal change PiperOrigin-RevId: 276126962 -- 4c840cab6a8d86efa29b397cafaf7520eece68cc by Andy Soffer : Update CMakeLists.txt to address https://github.com/abseil/abseil-cpp/issues/365. This does not cover every platform, but it does at least address the first-order issue of assuming gcc implies x86. PiperOrigin-RevId: 276116253 -- 98da366e6b5d51afe5d7ac6722126aca23d85ee6 by Abseil Team : Internal change PiperOrigin-RevId: 276097452 GitOrigin-RevId: e54b9c7bbb0c58475676c268e2e19c69f4bce48a Change-Id: I02d84454bb71ab21ad3d39650acf6cc6e36f58d7 --- absl/base/BUILD.bazel | 25 +++ absl/base/CMakeLists.txt | 26 +++ absl/base/config.h | 2 +- absl/base/internal/exponential_biased.cc | 84 ++++++++ absl/base/internal/exponential_biased.h | 77 +++++++ absl/base/internal/exponential_biased_test.cc | 168 +++++++++++++++ absl/base/optimization.h | 2 +- absl/container/BUILD.bazel | 1 + absl/container/CMakeLists.txt | 1 + absl/container/flat_hash_map.h | 2 +- absl/container/internal/hashtablez_sampler.cc | 83 ++------ absl/container/internal/raw_hash_set.h | 20 +- absl/container/internal/raw_hash_set_test.cc | 2 +- absl/copts/AbseilConfigureCopts.cmake | 25 ++- absl/random/BUILD.bazel | 2 +- absl/random/CMakeLists.txt | 17 +- absl/random/beta_distribution.h | 31 ++- absl/random/beta_distribution_test.cc | 2 +- absl/random/exponential_distribution.h | 14 +- absl/random/gaussian_distribution.h | 22 +- absl/random/internal/BUILD.bazel | 16 +- absl/random/internal/distribution_impl.h | 194 ------------------ absl/random/internal/generate_real.h | 144 +++++++++++++ ...ion_impl_test.cc => generate_real_test.cc} | 111 ++++++---- absl/random/log_uniform_int_distribution.h | 2 +- absl/random/poisson_distribution.h | 20 +- absl/random/uniform_int_distribution.h | 1 - absl/random/uniform_real_distribution.h | 15 +- 28 files changed, 739 insertions(+), 370 deletions(-) create mode 100644 absl/base/internal/exponential_biased.cc create mode 100644 absl/base/internal/exponential_biased.h create mode 100644 absl/base/internal/exponential_biased_test.cc delete mode 100644 absl/random/internal/distribution_impl.h create mode 100644 absl/random/internal/generate_real.h rename absl/random/internal/{distribution_impl_test.cc => generate_real_test.cc} (81%) diff --git a/absl/base/BUILD.bazel b/absl/base/BUILD.bazel index d74bac631a6..ffeca630706 100644 --- a/absl/base/BUILD.bazel +++ b/absl/base/BUILD.bazel @@ -557,6 +557,31 @@ cc_test( ], ) +cc_library( + name = "exponential_biased", + srcs = ["internal/exponential_biased.cc"], + hdrs = ["internal/exponential_biased.h"], + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = [ + "//absl:__subpackages__", + ], + deps = [":core_headers"], +) + +cc_test( + name = "exponential_biased_test", + size = "small", + srcs = ["internal/exponential_biased_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = ["//visibility:private"], + deps = [ + ":exponential_biased", + "//absl/strings", + "@com_google_googletest//:gtest_main", + ], +) + cc_library( name = "scoped_set_env", testonly = 1, diff --git a/absl/base/CMakeLists.txt b/absl/base/CMakeLists.txt index 79ee5b93ed7..2698213c566 100644 --- a/absl/base/CMakeLists.txt +++ b/absl/base/CMakeLists.txt @@ -503,6 +503,32 @@ absl_cc_test( gtest_main ) +absl_cc_library( + NAME + exponential_biased + SRCS + "internal/exponential_biased.cc" + HDRS + "internal/exponential_biased.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::core_headers +) + +absl_cc_test( + NAME + exponential_biased_test + SRCS + "internal/exponential_biased_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::exponential_biased + absl::strings + gmock_main +) + absl_cc_library( NAME scoped_set_env diff --git a/absl/base/config.h b/absl/base/config.h index 039b73d81f7..24851fa33ce 100644 --- a/absl/base/config.h +++ b/absl/base/config.h @@ -307,7 +307,7 @@ // ABSL_HAVE_SEMAPHORE_H // -// Checks whether the platform supports the header and sem_open(3) +// Checks whether the platform supports the header and sem_init(3) // family of functions as standardized in POSIX.1-2001. // // Note: While Apple provides for both iOS and macOS, it is diff --git a/absl/base/internal/exponential_biased.cc b/absl/base/internal/exponential_biased.cc new file mode 100644 index 00000000000..d7ffd184e96 --- /dev/null +++ b/absl/base/internal/exponential_biased.cc @@ -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. + +#include "absl/base/internal/exponential_biased.h" + +#include + +#include +#include +#include + +#include "absl/base/attributes.h" +#include "absl/base/optimization.h" + +namespace absl { +namespace base_internal { + +// The algorithm generates a random number between 0 and 1 and applies the +// inverse cumulative distribution function for an exponential. Specifically: +// Let m be the inverse of the sample period, then the probability +// distribution function is m*exp(-mx) so the CDF is +// p = 1 - exp(-mx), so +// q = 1 - p = exp(-mx) +// log_e(q) = -mx +// -log_e(q)/m = x +// log_2(q) * (-log_e(2) * 1/m) = x +// In the code, q is actually in the range 1 to 2**26, hence the -26 below +int64_t ExponentialBiased::Get(int64_t mean) { + if (ABSL_PREDICT_FALSE(!initialized_)) { + Initialize(); + } + + uint64_t rng = NextRandom(rng_); + rng_ = rng; + + // Take the top 26 bits as the random number + // (This plus the 1<<58 sampling bound give a max possible step of + // 5194297183973780480 bytes.) + // The uint32_t cast is to prevent a (hard-to-reproduce) NAN + // under piii debug for some binaries. + double q = static_cast(rng >> (kPrngNumBits - 26)) + 1.0; + // Put the computed p-value through the CDF of a geometric. + double interval = (std::log2(q) - 26) * (-std::log(2.0) * mean); + // Very large values of interval overflow int64_t. To avoid that, we will cheat + // and clamp any huge values to (int64_t max)/2. This is a potential source of + // bias, but the mean would need to be such a large value that it's not likely + // to come up. For example, with a mean of 1e18, the probability of hitting + // this condition is about 1/1000. For a mean of 1e17, standard calculators + // claim that this event won't happen. + if (interval > static_cast(std::numeric_limits::max() / 2)) { + return std::numeric_limits::max() / 2; + } + + return static_cast(interval); +} + +void ExponentialBiased::Initialize() { + // We don't get well distributed numbers from `this` so we call NextRandom() a + // bunch to mush the bits around. We use a global_rand to handle the case + // where the same thread (by memory address) gets created and destroyed + // repeatedly. + ABSL_CONST_INIT static std::atomic global_rand(0); + uint64_t r = reinterpret_cast(this) + + global_rand.fetch_add(1, std::memory_order_relaxed); + for (int i = 0; i < 20; ++i) { + r = NextRandom(r); + } + rng_ = r; + initialized_ = true; +} + +} // namespace base_internal +} // namespace absl diff --git a/absl/base/internal/exponential_biased.h b/absl/base/internal/exponential_biased.h new file mode 100644 index 00000000000..cac2d8ad84f --- /dev/null +++ b/absl/base/internal/exponential_biased.h @@ -0,0 +1,77 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_BASE_INTERNAL_EXPONENTIAL_BIASED_H_ +#define ABSL_BASE_INTERNAL_EXPONENTIAL_BIASED_H_ + +#include + +namespace absl { +namespace base_internal { + +// ExponentialBiased provides a small and fast random number generator for a +// rounded exponential distribution. This generator doesn't requires very little +// state doesn't impose synchronization overhead, which makes it useful in some +// specialized scenarios. +// +// For the generated variable X, X ~ floor(Exponential(1/mean)). The floor +// operation introduces a small amount of bias, but the distribution is useful +// to generate a wait time. That is, if an operation is supposed to happen on +// average to 1/mean events, then the generated variable X will describe how +// many events to skip before performing the operation and computing a new X. +// +// The mathematically precise distribution to use for integer wait times is a +// Geometric distribution, but a Geometric distribution takes slightly more time +// to compute and when the mean is large (say, 100+), the Geometric distribution +// is hard to distinguish from the result of ExponentialBiased. +// +// This class is thread-compatible. +class ExponentialBiased { + public: + // The number of bits set by NextRandom. + static constexpr int kPrngNumBits = 48; + + // Generates the floor of an exponentially distributed random variable by + // rounding the value down to the nearest integer. The result will be in the + // range [0, int64_t max / 2]. + int64_t Get(int64_t mean); + + // Computes a random number in the range [0, 1<<(kPrngNumBits+1) - 1] + // + // This is public to enable testing. + static uint64_t NextRandom(uint64_t rnd); + + private: + void Initialize(); + + uint64_t rng_{0}; + bool initialized_{false}; +}; + +// Returns the next prng value. +// pRNG is: aX+b mod c with a = 0x5DEECE66D, b = 0xB, c = 1<<48 +// This is the lrand64 generator. +inline uint64_t ExponentialBiased::NextRandom(uint64_t rnd) { + const uint64_t prng_mult = uint64_t{0x5DEECE66D}; + const uint64_t prng_add = 0xB; + const uint64_t prng_mod_power = 48; + const uint64_t prng_mod_mask = + ~((~static_cast(0)) << prng_mod_power); + return (prng_mult * rnd + prng_add) & prng_mod_mask; +} + +} // namespace base_internal +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_EXPONENTIAL_BIASED_H_ diff --git a/absl/base/internal/exponential_biased_test.cc b/absl/base/internal/exponential_biased_test.cc new file mode 100644 index 00000000000..09b511d14e7 --- /dev/null +++ b/absl/base/internal/exponential_biased_test.cc @@ -0,0 +1,168 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/base/internal/exponential_biased.h" + +#include + +#include +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/strings/str_cat.h" + +using ::testing::Ge; + +namespace absl { +namespace base_internal { + +MATCHER_P2(IsBetween, a, b, + absl::StrCat(std::string(negation ? "isn't" : "is"), " between ", a, + " and ", b)) { + return a <= arg && arg <= b; +} + +// Tests of the quality of the random numbers generated +// This uses the Anderson Darling test for uniformity. +// See "Evaluating the Anderson-Darling Distribution" by Marsaglia +// for details. + +// Short cut version of ADinf(z), z>0 (from Marsaglia) +// This returns the p-value for Anderson Darling statistic in +// the limit as n-> infinity. For finite n, apply the error fix below. +double AndersonDarlingInf(double z) { + if (z < 2) { + return exp(-1.2337141 / z) / sqrt(z) * + (2.00012 + + (0.247105 - + (0.0649821 - (0.0347962 - (0.011672 - 0.00168691 * z) * z) * z) * + z) * + z); + } + return exp( + -exp(1.0776 - + (2.30695 - + (0.43424 - (0.082433 - (0.008056 - 0.0003146 * z) * z) * z) * z) * + z)); +} + +// Corrects the approximation error in AndersonDarlingInf for small values of n +// Add this to AndersonDarlingInf to get a better approximation +// (from Marsaglia) +double AndersonDarlingErrFix(int n, double x) { + if (x > 0.8) { + return (-130.2137 + + (745.2337 - + (1705.091 - (1950.646 - (1116.360 - 255.7844 * x) * x) * x) * x) * + x) / + n; + } + double cutoff = 0.01265 + 0.1757 / n; + if (x < cutoff) { + double t = x / cutoff; + t = sqrt(t) * (1 - t) * (49 * t - 102); + return t * (0.0037 / (n * n) + 0.00078 / n + 0.00006) / n; + } else { + double t = (x - cutoff) / (0.8 - cutoff); + t = -0.00022633 + + (6.54034 - (14.6538 - (14.458 - (8.259 - 1.91864 * t) * t) * t) * t) * + t; + return t * (0.04213 + 0.01365 / n) / n; + } +} + +// Returns the AndersonDarling p-value given n and the value of the statistic +double AndersonDarlingPValue(int n, double z) { + double ad = AndersonDarlingInf(z); + double errfix = AndersonDarlingErrFix(n, ad); + return ad + errfix; +} + +double AndersonDarlingStatistic(const std::vector& random_sample) { + int n = random_sample.size(); + double ad_sum = 0; + for (int i = 0; i < n; i++) { + ad_sum += (2 * i + 1) * + std::log(random_sample[i] * (1 - random_sample[n - 1 - i])); + } + double ad_statistic = -n - 1 / static_cast(n) * ad_sum; + return ad_statistic; +} + +// Tests if the array of doubles is uniformly distributed. +// Returns the p-value of the Anderson Darling Statistic +// for the given set of sorted random doubles +// See "Evaluating the Anderson-Darling Distribution" by +// Marsaglia and Marsaglia for details. +double AndersonDarlingTest(const std::vector& random_sample) { + double ad_statistic = AndersonDarlingStatistic(random_sample); + double p = AndersonDarlingPValue(random_sample.size(), ad_statistic); + return p; +} + +// Testing that NextRandom generates uniform random numbers. Applies the +// Anderson-Darling test for uniformity +TEST(ExponentialBiasedTest, TestNextRandom) { + for (auto n : std::vector({ + 10, // Check short-range correlation + 100, 1000, + 10000 // Make sure there's no systemic error + })) { + uint64_t x = 1; + // This assumes that the prng returns 48 bit numbers + uint64_t max_prng_value = static_cast(1) << 48; + // Initialize. + for (int i = 1; i <= 20; i++) { + x = ExponentialBiased::NextRandom(x); + } + std::vector int_random_sample(n); + // Collect samples + for (int i = 0; i < n; i++) { + int_random_sample[i] = x; + x = ExponentialBiased::NextRandom(x); + } + // First sort them... + std::sort(int_random_sample.begin(), int_random_sample.end()); + std::vector random_sample(n); + // Convert them to uniform randoms (in the range [0,1]) + for (int i = 0; i < n; i++) { + random_sample[i] = + static_cast(int_random_sample[i]) / max_prng_value; + } + // Now compute the Anderson-Darling statistic + double ad_pvalue = AndersonDarlingTest(random_sample); + EXPECT_GT(std::min(ad_pvalue, 1 - ad_pvalue), 0.0001) + << "prng is not uniform: n = " << n << " p = " << ad_pvalue; + } +} + +// The generator needs to be available as a thread_local and as a static +// variable. +TEST(ExponentialBiasedTest, InitializationModes) { + ABSL_CONST_INIT static ExponentialBiased eb_static; + EXPECT_THAT(eb_static.Get(2), Ge(0)); + +#if ABSL_HAVE_THREAD_LOCAL + thread_local ExponentialBiased eb_thread; + EXPECT_THAT(eb_thread.Get(2), Ge(0)); +#endif + + ExponentialBiased eb_stack; + EXPECT_THAT(eb_stack.Get(2), Ge(0)); +} + +} // namespace base_internal +} // namespace absl diff --git a/absl/base/optimization.h b/absl/base/optimization.h index 0dcbef32a3c..646523b3461 100644 --- a/absl/base/optimization.h +++ b/absl/base/optimization.h @@ -172,7 +172,7 @@ #if ABSL_HAVE_BUILTIN(__builtin_expect) || \ (defined(__GNUC__) && !defined(__clang__)) #define ABSL_PREDICT_FALSE(x) (__builtin_expect(x, 0)) -#define ABSL_PREDICT_TRUE(x) (__builtin_expect(!!(x), 1)) +#define ABSL_PREDICT_TRUE(x) (__builtin_expect(false || (x), true)) #else #define ABSL_PREDICT_FALSE(x) (x) #define ABSL_PREDICT_TRUE(x) (x) diff --git a/absl/container/BUILD.bazel b/absl/container/BUILD.bazel index 0894bb207c6..e60979b2a54 100644 --- a/absl/container/BUILD.bazel +++ b/absl/container/BUILD.bazel @@ -493,6 +493,7 @@ cc_library( ":have_sse", "//absl/base", "//absl/base:core_headers", + "//absl/base:exponential_biased", "//absl/debugging:stacktrace", "//absl/memory", "//absl/synchronization", diff --git a/absl/container/CMakeLists.txt b/absl/container/CMakeLists.txt index 933c7a8c48a..aa33659b322 100644 --- a/absl/container/CMakeLists.txt +++ b/absl/container/CMakeLists.txt @@ -538,6 +538,7 @@ absl_cc_library( ${ABSL_DEFAULT_COPTS} DEPS absl::base + absl::exponential_biased absl::have_sse absl::synchronization ) diff --git a/absl/container/flat_hash_map.h b/absl/container/flat_hash_map.h index 5c16ac88fbb..283f2439676 100644 --- a/absl/container/flat_hash_map.h +++ b/absl/container/flat_hash_map.h @@ -401,7 +401,7 @@ class flat_hash_map : public absl::container_internal::raw_hash_map< // for the past-the-end iterator, which is invalidated. // // `swap()` requires that the flat hash map's hashing and key equivalence - // functions be Swappable, and are exchaged using unqualified calls to + // functions be Swappable, and are exchanged using unqualified calls to // non-member `swap()`. If the map's allocator has // `std::allocator_traits::propagate_on_container_swap::value` // set to `true`, the allocators are also exchanged using an unqualified call diff --git a/absl/container/internal/hashtablez_sampler.cc b/absl/container/internal/hashtablez_sampler.cc index 054e8981781..0a7ef61c8fe 100644 --- a/absl/container/internal/hashtablez_sampler.cc +++ b/absl/container/internal/hashtablez_sampler.cc @@ -21,6 +21,7 @@ #include #include "absl/base/attributes.h" +#include "absl/base/internal/exponential_biased.h" #include "absl/container/internal/have_sse.h" #include "absl/debugging/stacktrace.h" #include "absl/memory/memory.h" @@ -37,77 +38,13 @@ ABSL_CONST_INIT std::atomic g_hashtablez_enabled{ ABSL_CONST_INIT std::atomic g_hashtablez_sample_parameter{1 << 10}; ABSL_CONST_INIT std::atomic g_hashtablez_max_samples{1 << 20}; -// Returns the next pseudo-random value. -// pRNG is: aX+b mod c with a = 0x5DEECE66D, b = 0xB, c = 1<<48 -// This is the lrand64 generator. -uint64_t NextRandom(uint64_t rnd) { - const uint64_t prng_mult = uint64_t{0x5DEECE66D}; - const uint64_t prng_add = 0xB; - const uint64_t prng_mod_power = 48; - const uint64_t prng_mod_mask = ~(~uint64_t{0} << prng_mod_power); - return (prng_mult * rnd + prng_add) & prng_mod_mask; -} - -// Generates a geometric variable with the specified mean. -// This is done by generating a random number between 0 and 1 and applying -// the inverse cumulative distribution function for an exponential. -// Specifically: Let m be the inverse of the sample period, then -// the probability distribution function is m*exp(-mx) so the CDF is -// p = 1 - exp(-mx), so -// q = 1 - p = exp(-mx) -// log_e(q) = -mx -// -log_e(q)/m = x -// log_2(q) * (-log_e(2) * 1/m) = x -// In the code, q is actually in the range 1 to 2**26, hence the -26 below -// -int64_t GetGeometricVariable(int64_t mean) { #if ABSL_HAVE_THREAD_LOCAL - thread_local -#else // ABSL_HAVE_THREAD_LOCAL - // SampleSlow and hence GetGeometricVariable is guarded by a single mutex when - // there are not thread locals. Thus, a single global rng is acceptable for - // that case. - static -#endif // ABSL_HAVE_THREAD_LOCAL - uint64_t rng = []() { - // We don't get well distributed numbers from this so we call - // NextRandom() a bunch to mush the bits around. We use a global_rand - // to handle the case where the same thread (by memory address) gets - // created and destroyed repeatedly. - ABSL_CONST_INIT static std::atomic global_rand(0); - uint64_t r = reinterpret_cast(&rng) + - global_rand.fetch_add(1, std::memory_order_relaxed); - for (int i = 0; i < 20; ++i) { - r = NextRandom(r); - } - return r; - }(); - - rng = NextRandom(rng); - - // Take the top 26 bits as the random number - // (This plus the 1<<58 sampling bound give a max possible step of - // 5194297183973780480 bytes.) - const uint64_t prng_mod_power = 48; // Number of bits in prng - // The uint32_t cast is to prevent a (hard-to-reproduce) NAN - // under piii debug for some binaries. - double q = static_cast(rng >> (prng_mod_power - 26)) + 1.0; - // Put the computed p-value through the CDF of a geometric. - double interval = (log2(q) - 26) * (-std::log(2.0) * mean); - - // Very large values of interval overflow int64_t. If we happen to - // hit such improbable condition, we simply cheat and clamp interval - // to largest supported value. - if (interval > static_cast(std::numeric_limits::max() / 2)) { - return std::numeric_limits::max() / 2; - } - - // Small values of interval are equivalent to just sampling next time. - if (interval < 1) { - return 1; - } - return static_cast(interval); -} +thread_local absl::base_internal::ExponentialBiased + g_exponential_biased_generator; +#else +ABSL_CONST_INIT static absl::base_internal::ExponentialBiased + g_exponential_biased_generator; +#endif } // namespace @@ -253,8 +190,12 @@ HashtablezInfo* SampleSlow(int64_t* next_sample) { } bool first = *next_sample < 0; - *next_sample = GetGeometricVariable( + *next_sample = g_exponential_biased_generator.Get( g_hashtablez_sample_parameter.load(std::memory_order_relaxed)); + // Small values of interval are equivalent to just sampling next time. + if (*next_sample < 1) { + *next_sample = 1; + } // g_hashtablez_enabled can be dynamically flipped, we need to set a threshold // low enough that we will start sampling in a reasonable time, so we just use diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index 42b3c468113..9992ba4b8ac 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h @@ -614,13 +614,17 @@ class raw_hash_set { iterator() {} // PRECONDITION: not an end() iterator. - reference operator*() const { return PolicyTraits::element(slot_); } + reference operator*() const { + /* To be enabled: assert_is_full(); */ + return PolicyTraits::element(slot_); + } // PRECONDITION: not an end() iterator. pointer operator->() const { return &operator*(); } // PRECONDITION: not an end() iterator. iterator& operator++() { + /* To be enabled: assert_is_full(); */ ++ctrl_; ++slot_; skip_empty_or_deleted(); @@ -634,6 +638,8 @@ class raw_hash_set { } friend bool operator==(const iterator& a, const iterator& b) { + /* To be enabled: a.assert_is_valid(); */ + /* To be enabled: b.assert_is_valid(); */ return a.ctrl_ == b.ctrl_; } friend bool operator!=(const iterator& a, const iterator& b) { @@ -644,6 +650,11 @@ class raw_hash_set { iterator(ctrl_t* ctrl) : ctrl_(ctrl) {} // for end() iterator(ctrl_t* ctrl, slot_type* slot) : ctrl_(ctrl), slot_(slot) {} + void assert_is_full() const { assert(IsFull(*ctrl_)); } + void assert_is_valid() const { + assert(!ctrl_ || IsFull(*ctrl_) || *ctrl_ == kSentinel); + } + void skip_empty_or_deleted() { while (IsEmptyOrDeleted(*ctrl_)) { // ctrl is not necessarily aligned to Group::kWidth. It is also likely @@ -1155,7 +1166,7 @@ class raw_hash_set { // This overload is necessary because otherwise erase(const K&) would be // a better match if non-const iterator is passed as an argument. void erase(iterator it) { - assert(it != end()); + it.assert_is_full(); PolicyTraits::destroy(&alloc_ref(), it.slot_); erase_meta_only(it); } @@ -1172,12 +1183,14 @@ class raw_hash_set { template void merge(raw_hash_set& src) { // NOLINT assert(this != &src); - for (auto it = src.begin(), e = src.end(); it != e; ++it) { + for (auto it = src.begin(), e = src.end(); it != e;) { + auto next = std::next(it); if (PolicyTraits::apply(InsertSlot{*this, std::move(*it.slot_)}, PolicyTraits::element(it.slot_)) .second) { src.erase_meta_only(it); } + it = next; } } @@ -1187,6 +1200,7 @@ class raw_hash_set { } node_type extract(const_iterator position) { + position.inner_.assert_is_full(); auto node = CommonAccess::Transfer(alloc_ref(), position.inner_.slot_); erase_meta_only(position); diff --git a/absl/container/internal/raw_hash_set_test.cc b/absl/container/internal/raw_hash_set_test.cc index ed4ca8c88d3..33cfa72ce7a 100644 --- a/absl/container/internal/raw_hash_set_test.cc +++ b/absl/container/internal/raw_hash_set_test.cc @@ -1837,7 +1837,7 @@ TEST(TableDeathTest, EraseOfEndAsserts) { IntTable t; // Extra simple "regexp" as regexp support is highly varied across platforms. - constexpr char kDeathMsg[] = "it != end"; + constexpr char kDeathMsg[] = "IsFull"; EXPECT_DEATH_IF_SUPPORTED(t.erase(t.end()), kDeathMsg); } diff --git a/absl/copts/AbseilConfigureCopts.cmake b/absl/copts/AbseilConfigureCopts.cmake index b430873c5e0..b442646948a 100644 --- a/absl/copts/AbseilConfigureCopts.cmake +++ b/absl/copts/AbseilConfigureCopts.cmake @@ -5,10 +5,29 @@ set(ABSL_LSAN_LINKOPTS "") set(ABSL_HAVE_LSAN OFF) set(ABSL_DEFAULT_LINKOPTS "") +if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86_64") + if (MSVC) + set(ABSL_RANDOM_RANDEN_COPTS "${ABSL_RANDOM_HWAES_MSVC_X64_FLAGS}") + else() + set(ABSL_RANDOM_RANDEN_COPTS "${ABSL_RANDOM_HWAES_X64_FLAGS}") + endif() +elseif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "arm") + if ("${CMAKE_SIZEOF_VOID_P}" STREQUAL "8") + set(ABSL_RANDOM_RANDEN_COPTS "${ABSL_RANDOM_HWAES_ARM64_FLAGS}") + elseif("${CMAKE_SIZEOF_VOID_P}" STREQUAL "4") + set(ABSL_RANDOM_RANDEN_COPTS "${ABSL_RANDOM_HWAES_ARM32_FLAGS}") + else() + message(WARNING "Value of CMAKE_SIZEOF_VOID_P (${CMAKE_SIZEOF_VOID_P}) is not supported.") + endif() +else() + message(WARNING "Value of CMAKE_SYSTEM_PROCESSOR (${CMAKE_SYSTEM_PROCESSOR}) is unknown and cannot be used to set ABSL_RANDOM_RANDEN_COPTS") + set(ABSL_RANDOM_RANDEN_COPTS "") +endif() + + if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") set(ABSL_DEFAULT_COPTS "${ABSL_GCC_FLAGS}") set(ABSL_TEST_COPTS "${ABSL_GCC_FLAGS};${ABSL_GCC_TEST_FLAGS}") - set(ABSL_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) @@ -16,11 +35,9 @@ elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") set(ABSL_DEFAULT_COPTS "${ABSL_CLANG_CL_FLAGS}") set(ABSL_TEST_COPTS "${ABSL_CLANG_CL_FLAGS};${ABSL_CLANG_CL_TEST_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_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 @@ -34,12 +51,10 @@ 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_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_RANDOM_RANDEN_COPTS "") endif() if("${CMAKE_CXX_STANDARD}" EQUAL 98) diff --git a/absl/random/BUILD.bazel b/absl/random/BUILD.bazel index 828f1348bac..c3520df8dcc 100644 --- a/absl/random/BUILD.bazel +++ b/absl/random/BUILD.bazel @@ -69,10 +69,10 @@ cc_library( "//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:generate_real", "//absl/random/internal:iostream_state_saver", "//absl/random/internal:traits", "//absl/random/internal:uniform_helper", diff --git a/absl/random/CMakeLists.txt b/absl/random/CMakeLists.txt index 19dd2cabf57..289854ffd40 100644 --- a/absl/random/CMakeLists.txt +++ b/absl/random/CMakeLists.txt @@ -58,7 +58,7 @@ absl_cc_library( DEPS absl::base_internal absl::core_headers - absl::random_internal_distribution_impl + absl::random_internal_generate_real absl::random_internal_distributions absl::random_internal_fast_uniform_bits absl::random_internal_fastmath @@ -543,19 +543,18 @@ absl_cc_library( # Internal-only target, do not depend on directly. absl_cc_library( NAME - random_internal_distribution_impl + random_internal_generate_real HDRS - "internal/distribution_impl.h" + "internal/generate_real.h" COPTS ${ABSL_DEFAULT_COPTS} LINKOPTS ${ABSL_DEFAULT_LINKOPTS} DEPS absl::bits - absl::config - absl::int128 absl::random_internal_fastmath absl::random_internal_traits + absl::type_traits ) # Internal-only target, do not depend on directly. @@ -767,9 +766,9 @@ absl_cc_test( # Internal-only target, do not depend on directly. absl_cc_test( NAME - random_internal_distribution_impl_test + random_internal_generate_real_test SRCS - "internal/distribution_impl_test.cc" + "internal/generate_real_test.cc" COPTS ${ABSL_TEST_COPTS} LINKOPTS @@ -777,8 +776,7 @@ absl_cc_test( DEPS absl::bits absl::flags - absl::int128 - absl::random_internal_distribution_impl + absl::random_internal_generate_real gtest_main ) @@ -1029,7 +1027,6 @@ absl_cc_library( ${ABSL_DEFAULT_LINKOPTS} DEPS absl::core_headers - absl::random_internal_distribution_impl absl::random_internal_fast_uniform_bits absl::random_internal_iostream_state_saver absl::random_internal_traits diff --git a/absl/random/beta_distribution.h b/absl/random/beta_distribution.h index e29894f2037..b09b02f0dde 100644 --- a/absl/random/beta_distribution.h +++ b/absl/random/beta_distribution.h @@ -22,9 +22,10 @@ #include #include -#include "absl/random/internal/distribution_impl.h" +#include "absl/meta/type_traits.h" #include "absl/random/internal/fast_uniform_bits.h" #include "absl/random/internal/fastmath.h" +#include "absl/random/internal/generate_real.h" #include "absl/random/internal/iostream_state_saver.h" namespace absl { @@ -275,15 +276,21 @@ typename beta_distribution::result_type beta_distribution::AlgorithmJoehnk( URBG& g, // NOLINT(runtime/references) const param_type& p) { + using random_internal::GeneratePositiveTag; + using random_internal::GenerateRealFromBits; + using real_type = + absl::conditional_t::value, float, double>; + // Based on Joehnk, M. D. Erzeugung von betaverteilten und gammaverteilten // Zufallszahlen. Metrika 8.1 (1964): 5-15. // This method is described in Knuth, Vol 2 (Third Edition), pp 134. - using RandU64ToReal = typename random_internal::RandU64ToReal; - using random_internal::PositiveValueT; + result_type u, v, x, y, z; for (;;) { - u = RandU64ToReal::template Value(fast_u64_(g)); - v = RandU64ToReal::template Value(fast_u64_(g)); + u = GenerateRealFromBits( + fast_u64_(g)); + v = GenerateRealFromBits( + 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. @@ -327,12 +334,14 @@ typename beta_distribution::result_type beta_distribution::AlgorithmCheng( URBG& g, // NOLINT(runtime/references) const param_type& p) { + using random_internal::GeneratePositiveTag; + using random_internal::GenerateRealFromBits; + using real_type = + absl::conditional_t::value, float, double>; + // Based on Cheng, Russell CH. Generating beta variates with nonintegral // shape parameters. Communications of the ACM 21.4 (1978): 317-322. // (https://dl.acm.org/citation.cfm?id=359482). - using RandU64ToReal = typename random_internal::RandU64ToReal; - using random_internal::PositiveValueT; - static constexpr result_type kLogFour = result_type(1.3862943611198906188344642429163531361); // log(4) static constexpr result_type kS = @@ -341,8 +350,10 @@ beta_distribution::AlgorithmCheng( const bool use_algorithm_ba = (p.method_ == param_type::CHENG_BA); result_type u1, u2, v, w, z, r, s, t, bw_inv, lhs; for (;;) { - u1 = RandU64ToReal::template Value(fast_u64_(g)); - u2 = RandU64ToReal::template Value(fast_u64_(g)); + u1 = GenerateRealFromBits( + fast_u64_(g)); + u2 = GenerateRealFromBits( + 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); diff --git a/absl/random/beta_distribution_test.cc b/absl/random/beta_distribution_test.cc index 966ad08b5c9..d0111b3e3a8 100644 --- a/absl/random/beta_distribution_test.cc +++ b/absl/random/beta_distribution_test.cc @@ -92,7 +92,7 @@ TYPED_TEST(BetaDistributionInterfaceTest, SerializeTest) { for (TypeParam alpha : kValues) { for (TypeParam beta : kValues) { ABSL_INTERNAL_LOG( - INFO, absl::StrFormat("Smoke test for Beta(%f, %f)", alpha, beta)); + INFO, absl::StrFormat("Smoke test for Beta(%a, %a)", alpha, beta)); param_type param(alpha, beta); absl::beta_distribution before(alpha, beta); diff --git a/absl/random/exponential_distribution.h b/absl/random/exponential_distribution.h index c8af197575c..24abf57e99d 100644 --- a/absl/random/exponential_distribution.h +++ b/absl/random/exponential_distribution.h @@ -21,8 +21,9 @@ #include #include -#include "absl/random/internal/distribution_impl.h" +#include "absl/meta/type_traits.h" #include "absl/random/internal/fast_uniform_bits.h" +#include "absl/random/internal/generate_real.h" #include "absl/random/internal/iostream_state_saver.h" namespace absl { @@ -118,9 +119,14 @@ typename exponential_distribution::result_type exponential_distribution::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(fast_u64_(g)); + using random_internal::GenerateNegativeTag; + using random_internal::GenerateRealFromBits; + using real_type = + absl::conditional_t::value, float, double>; + + const result_type u = GenerateRealFromBits(fast_u64_(g)); // U(-1, 0) + // log1p(-x) is mathematically equivalent to log(1 - x) but has more // accuracy for x near zero. return p.neg_inv_lambda_ * std::log1p(u); diff --git a/absl/random/gaussian_distribution.h b/absl/random/gaussian_distribution.h index 1d1347bce0a..c299e9441c4 100644 --- a/absl/random/gaussian_distribution.h +++ b/absl/random/gaussian_distribution.h @@ -28,8 +28,8 @@ #include #include -#include "absl/random/internal/distribution_impl.h" #include "absl/random/internal/fast_uniform_bits.h" +#include "absl/random/internal/generate_real.h" #include "absl/random/internal/iostream_state_saver.h" namespace absl { @@ -207,12 +207,18 @@ namespace random_internal { template inline double gaussian_distribution_base::zignor_fallback(URBG& g, bool neg) { + using random_internal::GeneratePositiveTag; + using random_internal::GenerateRealFromBits; + // This fallback path happens approximately 0.05% of the time. double x, y; do { // kRInv = 1/r, U(0, 1) - x = kRInv * std::log(RandU64ToDouble(fast_u64_(g))); - y = -std::log(RandU64ToDouble(fast_u64_(g))); + x = kRInv * + std::log(GenerateRealFromBits( + fast_u64_(g))); + y = -std::log( + GenerateRealFromBits(fast_u64_(g))); } while ((y + y) < (x * x)); return neg ? (x - kR) : (kR - x); } @@ -220,6 +226,10 @@ inline double gaussian_distribution_base::zignor_fallback(URBG& g, bool neg) { template inline double gaussian_distribution_base::zignor( URBG& g) { // NOLINT(runtime/references) + using random_internal::GeneratePositiveTag; + using random_internal::GenerateRealFromBits; + using random_internal::GenerateSignedTag; + while (true) { // We use a single uint64_t to generate both a double and a strip. // These bits are unused when the generated double is > 1/2^5. @@ -227,7 +237,8 @@ inline double gaussian_distribution_base::zignor( // values (those smaller than 1/2^5, which all end up on the left tail). uint64_t bits = fast_u64_(g); int i = static_cast(bits & kMask); // pick a random strip - double j = RandU64ToDouble(bits); // U(-1, 1) + double j = GenerateRealFromBits( + bits); // U(-1, 1) const double x = j * zg_.x[i]; // Retangular box. Handles >97% of all cases. @@ -244,7 +255,8 @@ inline double gaussian_distribution_base::zignor( } // i > 0: Wedge samples using precomputed values. - double v = RandU64ToDouble(fast_u64_(g)); // U(0, 1) + double v = GenerateRealFromBits( + 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; diff --git a/absl/random/internal/BUILD.bazel b/absl/random/internal/BUILD.bazel index ec58cecd43e..91388d19f3c 100644 --- a/absl/random/internal/BUILD.bazel +++ b/absl/random/internal/BUILD.bazel @@ -175,9 +175,9 @@ cc_library( ) cc_library( - name = "distribution_impl", + name = "generate_real", hdrs = [ - "distribution_impl.h", + "generate_real.h", ], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, @@ -185,8 +185,7 @@ cc_library( ":fastmath", ":traits", "//absl/base:bits", - "//absl/base:config", - "//absl/numeric:int128", + "//absl/meta:type_traits", ], ) @@ -398,16 +397,17 @@ cc_test( ) cc_test( - name = "distribution_impl_test", + name = "generate_real_test", size = "small", - srcs = ["distribution_impl_test.cc"], + srcs = [ + "generate_real_test.cc", + ], copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ - ":distribution_impl", + ":generate_real", "//absl/base:bits", "//absl/flags:flag", - "//absl/numeric:int128", "@com_google_googletest//:gtest_main", ], ) diff --git a/absl/random/internal/distribution_impl.h b/absl/random/internal/distribution_impl.h deleted file mode 100644 index 49b3e1a6e33..00000000000 --- a/absl/random/internal/distribution_impl.h +++ /dev/null @@ -1,194 +0,0 @@ -// Copyright 2017 The Abseil Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef ABSL_RANDOM_INTERNAL_DISTRIBUTION_IMPL_H_ -#define ABSL_RANDOM_INTERNAL_DISTRIBUTION_IMPL_H_ - -// This file contains some implementation details which are used by one or more -// of the absl random number distributions. - -#include -#include -#include -#include -#include -#include - -#if (defined(_WIN32) || defined(_WIN64)) && defined(_M_IA64) -#include // 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 { -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(); -// -// When a value in U(-1,1) is required, use: -// RandU64ToDouble() => 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(); => U(0, 2) -// RandU64ToDouble(); => 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 -inline double RandU64ToDouble(uint64_t bits) { - static_assert(std::is_same::value || - std::is_same::value || - std::is_same::value, - ""); - - // Maybe use the left-most bit for a sign bit. - uint64_t sign = std::is_same::value - ? 0x8000000000000000ull - : 0; // Sign bits. - - if (std::is_same::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::value ? 1023U : 1022U) + - static_cast(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 -inline float RandU64ToFloat(uint64_t bits) { - static_assert(std::is_same::value || - std::is_same::value || - std::is_same::value, - ""); - - // Maybe use the left-most bit for a sign bit. - uint64_t sign = std::is_same::value - ? 0x80000000ul - : 0; // Sign bits. - - if (std::is_same::value) { - uint64_t a = bits & 0x8000000000000000ull; - sign = static_cast(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::value ? 127U : 126U) + - static_cast(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 -struct RandU64ToReal { - template - static inline Result Value(uint64_t bits) { - return RandU64ToDouble(bits); - } -}; - -template <> -struct RandU64ToReal { - template - static inline float Value(uint64_t bits) { - return RandU64ToFloat(bits); - } -}; - -} // namespace random_internal -} // namespace absl - -#endif // ABSL_RANDOM_INTERNAL_DISTRIBUTION_IMPL_H_ diff --git a/absl/random/internal/generate_real.h b/absl/random/internal/generate_real.h new file mode 100644 index 00000000000..246d863e56e --- /dev/null +++ b/absl/random/internal/generate_real.h @@ -0,0 +1,144 @@ +// Copyright 2017 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_RANDOM_INTERNAL_GENERATE_REAL_H_ +#define ABSL_RANDOM_INTERNAL_GENERATE_REAL_H_ + +// This file contains some implementation details which are used by one or more +// of the absl random number distributions. + +#include +#include +#include +#include + +#include "absl/base/internal/bits.h" +#include "absl/meta/type_traits.h" +#include "absl/random/internal/fastmath.h" +#include "absl/random/internal/traits.h" + +namespace absl { +namespace random_internal { + +// Tristate tag types controlling the output of GenerateRealFromBits. +struct GeneratePositiveTag {}; +struct GenerateNegativeTag {}; +struct GenerateSignedTag {}; + +// GenerateRealFromBits generates a single real value from a single 64-bit +// `bits` with template fields controlling the output. +// +// The `SignedTag` parameter controls whether positive, negative, +// or either signed/unsigned may be returned. +// When SignedTag == GeneratePositiveTag, range is U(0, 1) +// When SignedTag == GenerateNegativeTag, range is U(-1, 0) +// When SignedTag == GenerateSignedTag, range is U(-1, 1) +// +// When the `IncludeZero` parameter is true, the function may return 0 for some +// inputs, otherwise it never returns 0. +// +// When a value in U(0,1) is required, use: +// Uniform64ToReal; +// +// When a value in U(-1,1) is required, use: +// Uniform64ToReal; +// +// This generates more distinct values than the mathematical equivalent +// `U(0, 1) * 2.0 - 1.0`. +// +// Scaling the result by powers of 2 (and avoiding a multiply) is also possible: +// GenerateRealFromBits(..., -1); => U(0, 0.5) +// GenerateRealFromBits(..., 1); => U(0, 2) +// +template +inline RealType GenerateRealFromBits(uint64_t bits, int exp_bias = 0) { + using real_type = RealType; + using uint_type = absl::conditional_t::value, + uint32_t, uint64_t>; + + static_assert( + (std::is_same::value || + std::is_same::value), + "GenerateRealFromBits must be parameterized by either float or double."); + + static_assert(sizeof(uint_type) == sizeof(real_type), + "Mismatched unsinged and real types."); + + static_assert((std::numeric_limits::is_iec559 && + std::numeric_limits::radix == 2), + "RealType representation is not IEEE 754 binary."); + + static_assert((std::is_same::value || + std::is_same::value || + std::is_same::value), + ""); + + static constexpr int kExp = std::numeric_limits::digits - 1; + static constexpr uint_type kMask = (static_cast(1) << kExp) - 1u; + static constexpr int kUintBits = sizeof(uint_type) * 8; + + int exp = exp_bias + int{std::numeric_limits::max_exponent - 2}; + + // Determine the sign bit. + // Depending on the SignedTag, this may use the left-most bit + // or it may be a constant value. + uint_type sign = std::is_same::value + ? (static_cast(1) << (kUintBits - 1)) + : 0; + if (std::is_same::value) { + if (std::is_same::value) { + sign = bits & uint64_t{0x8000000000000000}; + } + if (std::is_same::value) { + const uint64_t tmp = bits & uint64_t{0x8000000000000000}; + sign = static_cast(tmp >> 32); + } + // adjust the bits and the exponent to account for removing + // the leading bit. + bits = bits & uint64_t{0x7FFFFFFFFFFFFFFF}; + exp++; + } + if (IncludeZero) { + if (bits == 0u) return 0; + } + + // Number of leading zeros is mapped to the exponent: 2^-clz + // bits is 0..01xxxxxx. After shifting, we're left with 1xxx...0..0 + int clz = base_internal::CountLeadingZeros64(bits); + bits <<= (IncludeZero ? clz : (clz & 63)); // remove 0-bits. + exp -= clz; // set the exponent. + bits >>= (63 - kExp); + + // Construct the 32-bit or 64-bit IEEE 754 floating-point value from + // the individual fields: sign, exp, mantissa(bits). + uint_type val = + (std::is_same::value ? 0u : sign) | + (static_cast(exp) << kExp) | + (static_cast(bits) & kMask); + + // bit_cast to the output-type + real_type result; + memcpy(static_cast(&result), static_cast(&val), + sizeof(result)); + return result; +} + +} // namespace random_internal +} // namespace absl + +#endif // ABSL_RANDOM_INTERNAL_GENERATE_REAL_H_ diff --git a/absl/random/internal/distribution_impl_test.cc b/absl/random/internal/generate_real_test.cc similarity index 81% rename from absl/random/internal/distribution_impl_test.cc rename to absl/random/internal/generate_real_test.cc index fcc169046d7..aa02f0c2c1b 100644 --- a/absl/random/internal/distribution_impl_test.cc +++ b/absl/random/internal/generate_real_test.cc @@ -12,57 +12,74 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "absl/random/internal/distribution_impl.h" +#include "absl/random/internal/generate_real.h" + +#include +#include +#include +#include #include "gtest/gtest.h" #include "absl/base/internal/bits.h" #include "absl/flags/flag.h" -#include "absl/numeric/int128.h" ABSL_FLAG(int64_t, absl_random_test_trials, 50000, "Number of trials for the probability tests."); -using absl::random_internal::NegativeValueT; -using absl::random_internal::PositiveValueT; -using absl::random_internal::RandU64ToDouble; -using absl::random_internal::RandU64ToFloat; -using absl::random_internal::SignedValueT; +using absl::random_internal::GenerateNegativeTag; +using absl::random_internal::GeneratePositiveTag; +using absl::random_internal::GenerateRealFromBits; +using absl::random_internal::GenerateSignedTag; namespace { -TEST(DistributionImplTest, U64ToFloat_Positive_NoZero_Test) { +TEST(GenerateRealTest, U64ToFloat_Positive_NoZero_Test) { auto ToFloat = [](uint64_t a) { - return RandU64ToFloat(a); + return GenerateRealFromBits(a); }; EXPECT_EQ(ToFloat(0x0000000000000000), 2.710505431e-20f); EXPECT_EQ(ToFloat(0x0000000000000001), 5.421010862e-20f); EXPECT_EQ(ToFloat(0x8000000000000000), 0.5); + EXPECT_EQ(ToFloat(0x8000000000000001), 0.5); EXPECT_EQ(ToFloat(0xFFFFFFFFFFFFFFFF), 0.9999999404f); } -TEST(DistributionImplTest, U64ToFloat_Positive_Zero_Test) { +TEST(GenerateRealTest, U64ToFloat_Positive_Zero_Test) { auto ToFloat = [](uint64_t a) { - return RandU64ToFloat(a); + return GenerateRealFromBits(a); }; EXPECT_EQ(ToFloat(0x0000000000000000), 0.0); EXPECT_EQ(ToFloat(0x0000000000000001), 5.421010862e-20f); EXPECT_EQ(ToFloat(0x8000000000000000), 0.5); + EXPECT_EQ(ToFloat(0x8000000000000001), 0.5); EXPECT_EQ(ToFloat(0xFFFFFFFFFFFFFFFF), 0.9999999404f); } -TEST(DistributionImplTest, U64ToFloat_Negative_NoZero_Test) { +TEST(GenerateRealTest, U64ToFloat_Negative_NoZero_Test) { auto ToFloat = [](uint64_t a) { - return RandU64ToFloat(a); + return GenerateRealFromBits(a); }; EXPECT_EQ(ToFloat(0x0000000000000000), -2.710505431e-20f); EXPECT_EQ(ToFloat(0x0000000000000001), -5.421010862e-20f); EXPECT_EQ(ToFloat(0x8000000000000000), -0.5); + EXPECT_EQ(ToFloat(0x8000000000000001), -0.5); EXPECT_EQ(ToFloat(0xFFFFFFFFFFFFFFFF), -0.9999999404f); } -TEST(DistributionImplTest, U64ToFloat_Signed_NoZero_Test) { +TEST(GenerateRealTest, U64ToFloat_Negative_Zero_Test) { auto ToFloat = [](uint64_t a) { - return RandU64ToFloat(a); + return GenerateRealFromBits(a); + }; + EXPECT_EQ(ToFloat(0x0000000000000000), 0.0); + EXPECT_EQ(ToFloat(0x0000000000000001), -5.421010862e-20f); + EXPECT_EQ(ToFloat(0x8000000000000000), -0.5); + EXPECT_EQ(ToFloat(0x8000000000000001), -0.5); + EXPECT_EQ(ToFloat(0xFFFFFFFFFFFFFFFF), -0.9999999404f); +} + +TEST(GenerateRealTest, U64ToFloat_Signed_NoZero_Test) { + auto ToFloat = [](uint64_t a) { + return GenerateRealFromBits(a); }; EXPECT_EQ(ToFloat(0x0000000000000000), 5.421010862e-20f); EXPECT_EQ(ToFloat(0x0000000000000001), 1.084202172e-19f); @@ -72,9 +89,9 @@ TEST(DistributionImplTest, U64ToFloat_Signed_NoZero_Test) { EXPECT_EQ(ToFloat(0xFFFFFFFFFFFFFFFF), -0.9999999404f); } -TEST(DistributionImplTest, U64ToFloat_Signed_Zero_Test) { +TEST(GenerateRealTest, U64ToFloat_Signed_Zero_Test) { auto ToFloat = [](uint64_t a) { - return RandU64ToFloat(a); + return GenerateRealFromBits(a); }; EXPECT_EQ(ToFloat(0x0000000000000000), 0); EXPECT_EQ(ToFloat(0x0000000000000001), 1.084202172e-19f); @@ -84,9 +101,9 @@ TEST(DistributionImplTest, U64ToFloat_Signed_Zero_Test) { EXPECT_EQ(ToFloat(0xFFFFFFFFFFFFFFFF), -0.9999999404f); } -TEST(DistributionImplTest, U64ToFloat_Signed_Bias_Test) { +TEST(GenerateRealTest, U64ToFloat_Signed_Bias_Test) { auto ToFloat = [](uint64_t a) { - return RandU64ToFloat(a); + return GenerateRealFromBits(a, 1); }; EXPECT_EQ(ToFloat(0x0000000000000000), 0); EXPECT_EQ(ToFloat(0x0000000000000001), 2 * 1.084202172e-19f); @@ -96,9 +113,9 @@ TEST(DistributionImplTest, U64ToFloat_Signed_Bias_Test) { EXPECT_EQ(ToFloat(0xFFFFFFFFFFFFFFFF), 2 * -0.9999999404f); } -TEST(DistributionImplTest, U64ToFloatTest) { +TEST(GenerateRealTest, U64ToFloatTest) { auto ToFloat = [](uint64_t a) -> float { - return RandU64ToFloat(a); + return GenerateRealFromBits(a); }; EXPECT_EQ(ToFloat(0x0000000000000000), 0.0f); @@ -150,44 +167,60 @@ TEST(DistributionImplTest, U64ToFloatTest) { } } -TEST(DistributionImplTest, U64ToDouble_Positive_NoZero_Test) { +TEST(GenerateRealTest, U64ToDouble_Positive_NoZero_Test) { auto ToDouble = [](uint64_t a) { - return RandU64ToDouble(a); + return GenerateRealFromBits(a); }; EXPECT_EQ(ToDouble(0x0000000000000000), 2.710505431213761085e-20); EXPECT_EQ(ToDouble(0x0000000000000001), 5.42101086242752217004e-20); EXPECT_EQ(ToDouble(0x0000000000000002), 1.084202172485504434e-19); EXPECT_EQ(ToDouble(0x8000000000000000), 0.5); + EXPECT_EQ(ToDouble(0x8000000000000001), 0.5); EXPECT_EQ(ToDouble(0xFFFFFFFFFFFFFFFF), 0.999999999999999888978); } -TEST(DistributionImplTest, U64ToDouble_Positive_Zero_Test) { +TEST(GenerateRealTest, U64ToDouble_Positive_Zero_Test) { auto ToDouble = [](uint64_t a) { - return RandU64ToDouble(a); + return GenerateRealFromBits(a); }; EXPECT_EQ(ToDouble(0x0000000000000000), 0.0); EXPECT_EQ(ToDouble(0x0000000000000001), 5.42101086242752217004e-20); EXPECT_EQ(ToDouble(0x8000000000000000), 0.5); + EXPECT_EQ(ToDouble(0x8000000000000001), 0.5); EXPECT_EQ(ToDouble(0xFFFFFFFFFFFFFFFF), 0.999999999999999888978); } -TEST(DistributionImplTest, U64ToDouble_Negative_NoZero_Test) { +TEST(GenerateRealTest, U64ToDouble_Negative_NoZero_Test) { auto ToDouble = [](uint64_t a) { - return RandU64ToDouble(a); + return GenerateRealFromBits(a); }; EXPECT_EQ(ToDouble(0x0000000000000000), -2.710505431213761085e-20); EXPECT_EQ(ToDouble(0x0000000000000001), -5.42101086242752217004e-20); EXPECT_EQ(ToDouble(0x0000000000000002), -1.084202172485504434e-19); EXPECT_EQ(ToDouble(0x8000000000000000), -0.5); + EXPECT_EQ(ToDouble(0x8000000000000001), -0.5); + EXPECT_EQ(ToDouble(0xFFFFFFFFFFFFFFFF), -0.999999999999999888978); +} + +TEST(GenerateRealTest, U64ToDouble_Negative_Zero_Test) { + auto ToDouble = [](uint64_t a) { + return GenerateRealFromBits(a); + }; + + EXPECT_EQ(ToDouble(0x0000000000000000), 0.0); + EXPECT_EQ(ToDouble(0x0000000000000001), -5.42101086242752217004e-20); + EXPECT_EQ(ToDouble(0x0000000000000002), -1.084202172485504434e-19); + EXPECT_EQ(ToDouble(0x8000000000000000), -0.5); + EXPECT_EQ(ToDouble(0x8000000000000001), -0.5); EXPECT_EQ(ToDouble(0xFFFFFFFFFFFFFFFF), -0.999999999999999888978); } -TEST(DistributionImplTest, U64ToDouble_Signed_NoZero_Test) { +TEST(GenerateRealTest, U64ToDouble_Signed_NoZero_Test) { auto ToDouble = [](uint64_t a) { - return RandU64ToDouble(a); + return GenerateRealFromBits(a); }; EXPECT_EQ(ToDouble(0x0000000000000000), 5.42101086242752217004e-20); @@ -198,9 +231,9 @@ TEST(DistributionImplTest, U64ToDouble_Signed_NoZero_Test) { EXPECT_EQ(ToDouble(0xFFFFFFFFFFFFFFFF), -0.999999999999999888978); } -TEST(DistributionImplTest, U64ToDouble_Signed_Zero_Test) { +TEST(GenerateRealTest, U64ToDouble_Signed_Zero_Test) { auto ToDouble = [](uint64_t a) { - return RandU64ToDouble(a); + return GenerateRealFromBits(a); }; EXPECT_EQ(ToDouble(0x0000000000000000), 0); EXPECT_EQ(ToDouble(0x0000000000000001), 1.084202172485504434e-19); @@ -210,9 +243,9 @@ TEST(DistributionImplTest, U64ToDouble_Signed_Zero_Test) { EXPECT_EQ(ToDouble(0xFFFFFFFFFFFFFFFF), -0.999999999999999888978); } -TEST(DistributionImplTest, U64ToDouble_Signed_Bias_Test) { +TEST(GenerateRealTest, U64ToDouble_GenerateSignedTag_Bias_Test) { auto ToDouble = [](uint64_t a) { - return RandU64ToDouble(a); + return GenerateRealFromBits(a, -1); }; EXPECT_EQ(ToDouble(0x0000000000000000), 0); EXPECT_EQ(ToDouble(0x0000000000000001), 1.084202172485504434e-19 / 2); @@ -222,9 +255,9 @@ TEST(DistributionImplTest, U64ToDouble_Signed_Bias_Test) { EXPECT_EQ(ToDouble(0xFFFFFFFFFFFFFFFF), -0.999999999999999888978 / 2); } -TEST(DistributionImplTest, U64ToDoubleTest) { +TEST(GenerateRealTest, U64ToDoubleTest) { auto ToDouble = [](uint64_t a) { - return RandU64ToDouble(a); + return GenerateRealFromBits(a); }; EXPECT_EQ(ToDouble(0x0000000000000000), 0.0); @@ -296,9 +329,9 @@ TEST(DistributionImplTest, U64ToDoubleTest) { } } -TEST(DistributionImplTest, U64ToDoubleSignedTest) { +TEST(GenerateRealTest, U64ToDoubleSignedTest) { auto ToDouble = [](uint64_t a) { - return RandU64ToDouble(a); + return GenerateRealFromBits(a); }; EXPECT_EQ(ToDouble(0x0000000000000000), 5.42101086242752217004e-20); @@ -379,10 +412,10 @@ TEST(DistributionImplTest, U64ToDoubleSignedTest) { } } -TEST(DistributionImplTest, ExhaustiveFloat) { +TEST(GenerateRealTest, ExhaustiveFloat) { using absl::base_internal::CountLeadingZeros64; auto ToFloat = [](uint64_t a) { - return RandU64ToFloat(a); + return GenerateRealFromBits(a); }; // Rely on RandU64ToFloat generating values from greatest to least when diff --git a/absl/random/log_uniform_int_distribution.h b/absl/random/log_uniform_int_distribution.h index ac43416ee57..956a69070cc 100644 --- a/absl/random/log_uniform_int_distribution.h +++ b/absl/random/log_uniform_int_distribution.h @@ -23,8 +23,8 @@ #include #include -#include "absl/random/internal/distribution_impl.h" #include "absl/random/internal/fastmath.h" +#include "absl/random/internal/generate_real.h" #include "absl/random/internal/iostream_state_saver.h" #include "absl/random/internal/traits.h" #include "absl/random/uniform_int_distribution.h" diff --git a/absl/random/poisson_distribution.h b/absl/random/poisson_distribution.h index 7750b1c9ba5..23a953ff253 100644 --- a/absl/random/poisson_distribution.h +++ b/absl/random/poisson_distribution.h @@ -22,9 +22,9 @@ #include #include -#include "absl/random/internal/distribution_impl.h" #include "absl/random/internal/fast_uniform_bits.h" #include "absl/random/internal/fastmath.h" +#include "absl/random/internal/generate_real.h" #include "absl/random/internal/iostream_state_saver.h" namespace absl { @@ -164,9 +164,9 @@ typename poisson_distribution::result_type poisson_distribution::operator()( URBG& g, // NOLINT(runtime/references) const param_type& p) { - using random_internal::PositiveValueT; - using random_internal::RandU64ToDouble; - using random_internal::SignedValueT; + using random_internal::GeneratePositiveTag; + using random_internal::GenerateRealFromBits; + using random_internal::GenerateSignedTag; if (p.split_ != 0) { // Use Knuth's algorithm with range splitting to avoid floating-point @@ -186,7 +186,8 @@ poisson_distribution::operator()( for (int split = p.split_; split > 0; --split) { double r = 1.0; do { - r *= RandU64ToDouble(fast_u64_(g)); + r *= GenerateRealFromBits( + fast_u64_(g)); // U(-1, 0) ++n; } while (r > p.emu_); --n; @@ -205,10 +206,11 @@ poisson_distribution::operator()( // and k = max(f). const double a = p.mean_ + 0.5; for (;;) { - const double u = - RandU64ToDouble(fast_u64_(g)); // (0, 1) - const double v = - RandU64ToDouble(fast_u64_(g)); // (-1, 1) + const double u = GenerateRealFromBits( + fast_u64_(g)); // U(0, 1) + const double v = GenerateRealFromBits( + fast_u64_(g)); // U(-1, 1) + const double x = std::floor(p.s_ * v / u + a); if (x < 0) continue; // f(negative) = 0 const double rhs = x * p.lmu_; diff --git a/absl/random/uniform_int_distribution.h b/absl/random/uniform_int_distribution.h index 02aa376555e..dc8ba8c1e2a 100644 --- a/absl/random/uniform_int_distribution.h +++ b/absl/random/uniform_int_distribution.h @@ -34,7 +34,6 @@ #include #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" diff --git a/absl/random/uniform_real_distribution.h b/absl/random/uniform_real_distribution.h index 336abb3907b..bf2ed2c5ad3 100644 --- a/absl/random/uniform_real_distribution.h +++ b/absl/random/uniform_real_distribution.h @@ -39,8 +39,9 @@ #include #include -#include "absl/random/internal/distribution_impl.h" +#include "absl/meta/type_traits.h" #include "absl/random/internal/fast_uniform_bits.h" +#include "absl/random/internal/generate_real.h" #include "absl/random/internal/iostream_state_saver.h" namespace absl { @@ -76,6 +77,7 @@ class uniform_real_distribution { // is not possible, so value generation cannot use the full range of the // real type. assert(range_ <= (std::numeric_limits::max)()); + assert(std::isfinite(range_)); } result_type a() const { return lo_; } @@ -151,10 +153,15 @@ template typename uniform_real_distribution::result_type uniform_real_distribution::operator()( URBG& gen, const param_type& p) { // NOLINT(runtime/references) - using random_internal::PositiveValueT; + using random_internal::GeneratePositiveTag; + using random_internal::GenerateRealFromBits; + using real_type = + absl::conditional_t::value, float, double>; + while (true) { - const result_type sample = random_internal::RandU64ToReal< - result_type>::template Value(fast_u64_(gen)); + const result_type sample = + GenerateRealFromBits( + 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; From 39d68a422a2c08bc7bea56487aded3da8cbf0a19 Mon Sep 17 00:00:00 2001 From: Nao YONASHIRO Date: Thu, 31 Oct 2019 00:01:11 +0900 Subject: [PATCH 0011/3164] docs: fix typo (#397) --- absl/container/internal/raw_hash_set.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index 9992ba4b8ac..9079a73e52a 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h @@ -668,7 +668,7 @@ class raw_hash_set { } ctrl_t* ctrl_ = nullptr; - // To avoid uninitialized member warnigs, put slot_ in an anonymous union. + // To avoid uninitialized member warnings, put slot_ in an anonymous union. // The member is not initialized on singleton and end iterators. union { slot_type* slot_; From 8207907f4f7fbaeeaa2b7340c76875e06fd345ba Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Wed, 30 Oct 2019 08:00:44 -0700 Subject: [PATCH 0012/3164] Export of internal Abseil changes -- 2f49cb9009386bc67bf54a2908c8720b749c8b7f by Greg Falcon : docs: fix typo Import of https://github.com/abseil/abseil-cpp/pull/397 PiperOrigin-RevId: 277504420 -- f2bed362c1c12d3fa9c22d11f2b918668e8c37b7 by Abseil Team : Avoid our is_[copy/move]_assignable workarounds in MSVC 19.20 and on, since that release introduces a regression that breaks our workaround. We should ideally use the std forms in more cases, but branching when our workarounds fail is simpler to maintain. PiperOrigin-RevId: 277502334 -- e33de894ffd49848b9e088f59acc9743d1661948 by Derek Mauro : Update rules_cc version. The mirror.bazel.build URL does not exist (cache expiration?) PiperOrigin-RevId: 277498394 -- b23757b0747c64634d2d701433782c969effef19 by Abseil Team : Fix https://github.com/abseil/abseil-cpp/issues/394. PiperOrigin-RevId: 277491405 -- 54c75b8b29813531c52d67cf0ba7063baae4a4f3 by Abseil Team : Fix comment typos: waker => waiter. PiperOrigin-RevId: 277376952 -- 874eeaa3b3af808fc88b6355245f643674f5e36e by Abseil Team : Don't use atomic ops on waiter and wakeup counts in CONDVAR waiter mode. Just guard the waiter and wakeup counts with the mutex. This eliminates the race. Also fix a typo in the error message for pthread_cond_timedwait. PiperOrigin-RevId: 277366017 -- ce8c9a63109214519b5a7eaecef2c663c4d566df by Greg Falcon : Implement the config options for our four main C++ forward compatibility types. These options control whether the names `any`, `optional`, `string_view`, and `variant` in namespace `absl` are aliases to the corresponding C++17 types in namespace `std`. By default, we continue to auto-detect the configuration of the compiler being run. These options are not intended to be modified on the command line (as -D flags, say). Instead, the options.h file can be modified by distributors of Abseil (e.g., binary packagers, maintainers of local copies of Abseil, etc.) Changing options will change Abseil in an ODR sense. Any program must only link in a single version of Abseil. Linking libraries that use Abseil configured with different options is an error: there is no ABI compatibility guarantee when linking different configurations, even if the Abseil versions used are otherwise exactly identical. PiperOrigin-RevId: 277364298 -- 5ed3ad42ae43a05862070f92f9ffd07f5c1f2b81 by Chris Kennelly : Suppress -Wimplicit-int-float-conversion. On recent builds of Clang, this is an error/warning. PiperOrigin-RevId: 277346168 -- 9b9b0055243c705189bb27d912e6d45a7789cdee by Eric Fiselier : Allow building Abseil as a shared library with CMake. By default CMake's `add_library` creates the target as a static library. However, users can override the default using the builtin CMake option -DBUILD_SHARED_LIBS=ON. This changes Abseil's CMake to respect this configuration option by removing the explicit `STATIC` in our usages of `add_library`. PiperOrigin-RevId: 277337753 -- 63a8b7b8ede3a9d851916929d6b8537f4f2508ca by Abseil Team : Improve AlphaNum Hex performance by using absl::numbers_internal::FastHexToBufferZeroPad16. PiperOrigin-RevId: 277318108 -- dd047f7e92032682d94b27732df0e4d0670e24a4 by CJ Johnson : Internal change PiperOrigin-RevId: 277316913 -- d19ee7074929fed08973cc5b40a844573ce1e0a6 by Abseil Team : Handle invoking [[nodiscard]] functions correctly in our tests. PiperOrigin-RevId: 277301500 -- 5373f3737894ba9b8481e95e5f58c7957c00d26a by Chris Kennelly : For internal reasons, loosen visibility restrictions of `//absl/base:malloc_internal`. As an internal-namespace interface, this module remains unsupported. We reserve the right to change, delete, or re-restrict visibility to this target at any time. PiperOrigin-RevId: 277118689 -- 44e4f6655e05393a375d09b3625c192b1fde5909 by Abseil Team : Fix error in example civil day comment. PiperOrigin-RevId: 277103315 -- 7657392b4ce48469106f11cdb952a0bc76a28df3 by Abseil Team : Internal change PiperOrigin-RevId: 277056076 -- c75bda76f40b01fa249b75b5a70c1f5907e56c76 by Abseil Team : Suppress lifetime constant-initialization tests when building with MSVC versions > 19.0. These are broken due to non-compliant initialization order in these versions: https://developercommunity.visualstudio.com/content/problem/336946/class-with-constexpr-constructor-not-using-static.html We don't know when Microsoft will fix this bug. PiperOrigin-RevId: 277049770 -- 16c3b9bf2a1796efa57f97b00bcd6227fbccca1f by Matt Calabrese : Avoid our is_[copy/move]_assignable workarounds in MSVC 19.20 and on, since that release introduces a regression that breaks our workaround. We should ideally use the std forms in more cases, but branching when our workarounds fail is simpler to maintain. PiperOrigin-RevId: 277048670 -- e91003fa3ee6026d9b80624a23fc144fa5d74810 by Chris Kennelly : Fix -Wimplicit-int-float-conversion warning in latest clang PiperOrigin-RevId: 276771618 -- 53087ca6603e86ad815f3dd9ab795cc0f79792c1 by Andy Soffer : Add documentation on absl::SNPrintF. PiperOrigin-RevId: 276694032 -- a9426af8cbd4c3a8f9053e7446c821852e41ff61 by Jorg Brown : Stop including kern/OSByteOrder.h in order to support __APPLE__ Apple compiles with clang now anyway, and clang has support for the built-in compiler swap functions that are much faster than any function call to the OS. PiperOrigin-RevId: 276625231 -- df974be5aa5b4dc1b09c356cb8816edfc7867e63 by Jorg Brown : Fix the build for Android x86-64 builds, where __SSE4_2__ is defined but _bswap64 is not. PiperOrigin-RevId: 276542642 -- d99dc092b3a5ad17643005e55f3b3cb6b9187ccc by Jorg Brown : Remove a byteswap from the non-SSE path of FastHexToBufferZeroPad16 Remove the need for including absl/base/internal/endian.h from the SSE case (since if we have the Intel SSE intrinsics, then clearly we also have the Intel Byte-Swap intrinsics.) PiperOrigin-RevId: 276532608 -- d67b106dc930d8558810ae3983613bb2ab1e0d36 by Abseil Team : Use explicit static_cast for int64_t to double conversion This uses an explicit static_cast() in the int64_t to double comparisons in duration.cc's SafeAddRepHi. This satisfies clang's -Wimplicit-int-to-float-conversion warning (with https://reviews.llvm.org/D64666). This may also make it easier for humans to realize that the comparison is happening between two floating point double precision values. It should have no impact on the behavior or generated code. Tested: No behavior change PiperOrigin-RevId: 276529211 GitOrigin-RevId: 2f49cb9009386bc67bf54a2908c8720b749c8b7f Change-Id: I71e0781893ce219960b8290d54b20532779cb0ff --- CMake/AbseilHelpers.cmake | 5 +- WORKSPACE | 8 +- absl/base/BUILD.bazel | 3 +- absl/base/CMakeLists.txt | 1 + absl/base/config.h | 63 +++++++ absl/base/internal/endian.h | 8 - absl/base/options.h | 188 +++++++++++++++++++ absl/container/flat_hash_map_test.cc | 4 +- absl/container/internal/inlined_vector.h | 29 +-- absl/container/internal/raw_hash_set.h | 2 +- absl/meta/type_traits.h | 14 ++ absl/random/internal/nonsecure_base_test.cc | 7 +- absl/random/log_uniform_int_distribution.h | 16 +- absl/strings/numbers.h | 13 +- absl/strings/str_cat.cc | 26 ++- absl/strings/str_format.h | 6 + absl/strings/string_view.cc | 4 +- absl/strings/string_view.h | 6 +- absl/strings/string_view_test.cc | 12 +- absl/synchronization/internal/waiter.cc | 56 +++--- absl/synchronization/internal/waiter.h | 7 +- absl/synchronization/lifetime_test.cc | 6 + absl/time/civil_time.h | 2 +- absl/time/duration.cc | 4 +- absl/time/time.h | 4 +- absl/types/any.h | 6 +- absl/types/any_exception_safety_test.cc | 4 +- absl/types/any_test.cc | 10 +- absl/types/bad_any_cast.cc | 4 +- absl/types/bad_any_cast.h | 6 +- absl/types/bad_optional_access.cc | 4 +- absl/types/bad_optional_access.h | 6 +- absl/types/bad_variant_access.cc | 4 +- absl/types/bad_variant_access.h | 6 +- absl/types/internal/variant.h | 4 +- absl/types/optional.h | 6 +- absl/types/optional_exception_safety_test.cc | 4 +- absl/types/optional_test.cc | 20 +- absl/types/variant.h | 6 +- absl/types/variant_exception_safety_test.cc | 16 +- absl/types/variant_test.cc | 34 ++-- absl/utility/utility.h | 14 +- 42 files changed, 459 insertions(+), 189 deletions(-) create mode 100644 absl/base/options.h diff --git a/CMake/AbseilHelpers.cmake b/CMake/AbseilHelpers.cmake index 67b97fe14df..28fefaa67ef 100644 --- a/CMake/AbseilHelpers.cmake +++ b/CMake/AbseilHelpers.cmake @@ -104,7 +104,10 @@ function(absl_cc_library) endif() if(NOT ABSL_CC_LIB_IS_INTERFACE) - add_library(${_NAME} STATIC "") + # CMake creates static libraries by default. Users can specify + # -DBUILD_SHARED_LIBS=ON during initial configuration to build shared + # libraries instead. + add_library(${_NAME} "") target_sources(${_NAME} PRIVATE ${ABSL_CC_LIB_SRCS} ${ABSL_CC_LIB_HDRS}) target_include_directories(${_NAME} PUBLIC diff --git a/WORKSPACE b/WORKSPACE index 572f5b1f7fa..f2b10464467 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -36,10 +36,10 @@ http_archive( # C++ rules for Bazel. http_archive( name = "rules_cc", - sha256 = "67412176974bfce3f4cf8bdaff39784a72ed709fc58def599d1f68710b58d68b", - strip_prefix = "rules_cc-b7fe9697c0c76ab2fd431a891dbb9a6a32ed7c3e", + sha256 = "9a446e9dd9c1bb180c86977a8dc1e9e659550ae732ae58bd2e8fd51e15b2c91d", + strip_prefix = "rules_cc-262ebec3c2296296526740db4aefce68c80de7fa", urls = [ - "https://mirror.bazel.build/github.com/bazelbuild/rules_cc/archive/b7fe9697c0c76ab2fd431a891dbb9a6a32ed7c3e.zip", - "https://github.com/bazelbuild/rules_cc/archive/b7fe9697c0c76ab2fd431a891dbb9a6a32ed7c3e.zip", + "https://mirror.bazel.build/github.com/bazelbuild/rules_cc/archive/262ebec3c2296296526740db4aefce68c80de7fa.zip", + "https://github.com/bazelbuild/rules_cc/archive/262ebec3c2296296526740db4aefce68c80de7fa.zip", ], ) diff --git a/absl/base/BUILD.bazel b/absl/base/BUILD.bazel index ffeca630706..6fc712d968f 100644 --- a/absl/base/BUILD.bazel +++ b/absl/base/BUILD.bazel @@ -87,6 +87,7 @@ cc_library( name = "config", hdrs = [ "config.h", + "options.h", "policy_checks.h", ], copts = ABSL_DEFAULT_COPTS, @@ -137,7 +138,7 @@ cc_library( "//conditions:default": ["-pthread"], }) + ABSL_DEFAULT_LINKOPTS, visibility = [ - "//absl:__subpackages__", + "//visibility:public", ], deps = [ ":base", diff --git a/absl/base/CMakeLists.txt b/absl/base/CMakeLists.txt index 2698213c566..9550cdb2db4 100644 --- a/absl/base/CMakeLists.txt +++ b/absl/base/CMakeLists.txt @@ -75,6 +75,7 @@ absl_cc_library( config HDRS "config.h" + "options.h" "policy_checks.h" COPTS ${ABSL_DEFAULT_COPTS} diff --git a/absl/base/config.h b/absl/base/config.h index 24851fa33ce..46a84f1a76f 100644 --- a/absl/base/config.h +++ b/absl/base/config.h @@ -63,6 +63,7 @@ #include #endif +#include "absl/base/options.h" #include "absl/base/policy_checks.h" // ----------------------------------------------------------------------------- @@ -468,6 +469,68 @@ #define ABSL_HAVE_STD_STRING_VIEW 1 #endif +// ABSL_USES_STD_ANY +// +// Indicates whether absl::any is an alias for std::any. +#if !defined(ABSL_OPTION_USE_STD_ANY) +#error options.h is misconfigured. +#elif ABSL_OPTION_USE_STD_ANY == 0 || \ + (ABSL_OPTION_USE_STD_ANY == 2 && !defined(ABSL_HAVE_STD_ANY)) +#undef ABSL_USES_STD_ANY +#elif ABSL_OPTION_USE_STD_ANY == 1 || \ + (ABSL_OPTION_USE_STD_ANY == 2 && defined(ABSL_HAVE_STD_ANY)) +#define ABSL_USES_STD_ANY 1 +#else +#error options.h is misconfigured. +#endif + +// ABSL_USES_STD_OPTIONAL +// +// Indicates whether absl::optional is an alias for std::optional. +#if !defined(ABSL_OPTION_USE_STD_OPTIONAL) +#error options.h is misconfigured. +#elif ABSL_OPTION_USE_STD_OPTIONAL == 0 || \ + (ABSL_OPTION_USE_STD_OPTIONAL == 2 && !defined(ABSL_HAVE_STD_OPTIONAL)) +#undef ABSL_USES_STD_OPTIONAL +#elif ABSL_OPTION_USE_STD_OPTIONAL == 1 || \ + (ABSL_OPTION_USE_STD_OPTIONAL == 2 && defined(ABSL_HAVE_STD_OPTIONAL)) +#define ABSL_USES_STD_OPTIONAL 1 +#else +#error options.h is misconfigured. +#endif + +// ABSL_USES_STD_VARIANT +// +// Indicates whether absl::variant is an alias for std::variant. +#if !defined(ABSL_OPTION_USE_STD_VARIANT) +#error options.h is misconfigured. +#elif ABSL_OPTION_USE_STD_VARIANT == 0 || \ + (ABSL_OPTION_USE_STD_VARIANT == 2 && !defined(ABSL_HAVE_STD_VARIANT)) +#undef ABSL_USES_STD_VARIANT +#elif ABSL_OPTION_USE_STD_VARIANT == 1 || \ + (ABSL_OPTION_USE_STD_VARIANT == 2 && defined(ABSL_HAVE_STD_VARIANT)) +#define ABSL_USES_STD_VARIANT 1 +#else +#error options.h is misconfigured. +#endif + +// ABSL_USES_STD_STRING_VIEW +// +// Indicates whether absl::string_view is an alias for std::string_view. +#if !defined(ABSL_OPTION_USE_STD_STRING_VIEW) +#error options.h is misconfigured. +#elif ABSL_OPTION_USE_STD_STRING_VIEW == 0 || \ + (ABSL_OPTION_USE_STD_STRING_VIEW == 2 && \ + !defined(ABSL_HAVE_STD_STRING_VIEW)) +#undef ABSL_USES_STD_STRING_VIEW +#elif ABSL_OPTION_USE_STD_STRING_VIEW == 1 || \ + (ABSL_OPTION_USE_STD_STRING_VIEW == 2 && \ + defined(ABSL_HAVE_STD_STRING_VIEW)) +#define ABSL_USES_STD_STRING_VIEW 1 +#else +#error options.h is misconfigured. +#endif + // In debug mode, MSVC 2017's std::variant throws a EXCEPTION_ACCESS_VIOLATION // SEH exception from emplace for variant when constructing the // struct can throw. This defeats some of variant_test and diff --git a/absl/base/internal/endian.h b/absl/base/internal/endian.h index b43c9a46ee3..8233cb52433 100644 --- a/absl/base/internal/endian.h +++ b/absl/base/internal/endian.h @@ -19,9 +19,6 @@ // The following guarantees declaration of the byte swap functions #ifdef _MSC_VER #include // NOLINT(build/include) -#elif defined(__APPLE__) -// macOS / Darwin features -#include #elif defined(__FreeBSD__) #include #elif defined(__GLIBC__) @@ -63,11 +60,6 @@ inline uint16_t gbswap_16(uint16_t host_int) { return _byteswap_ushort(host_int); } -#elif defined(__APPLE__) -inline uint64_t gbswap_64(uint64_t host_int) { return OSSwapInt16(host_int); } -inline uint32_t gbswap_32(uint32_t host_int) { return OSSwapInt32(host_int); } -inline uint16_t gbswap_16(uint16_t host_int) { return OSSwapInt64(host_int); } - #else inline uint64_t gbswap_64(uint64_t host_int) { #if defined(__GNUC__) && defined(__x86_64__) && !defined(__APPLE__) diff --git a/absl/base/options.h b/absl/base/options.h new file mode 100644 index 00000000000..3961e63f4e2 --- /dev/null +++ b/absl/base/options.h @@ -0,0 +1,188 @@ +#ifndef ABSL_BASE_OPTIONS_H_ +#define ABSL_BASE_OPTIONS_H_ + +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: options.h +// ----------------------------------------------------------------------------- +// +// This file contains Abseil configuration options for setting specific +// implementations instead of letting Abseil determine which implementation to +// use at compile-time. Setting these options may be useful for package or build +// managers who wish to guarantee ABI stability within binary builds (which are +// otherwise difficult to enforce). +// +// *** IMPORTANT NOTICE FOR PACKAGE MANAGERS: It is important that +// maintainers of package managers who wish to package Abseil read and +// understand this file! *** +// +// Abseil contains a number of possible configuration endpoints, based on +// parameters such as the detected platform, language version, or command-line +// flags used to invoke the underlying binary. As is the case with all +// libraries, binaries which contain Abseil code must ensure that separate +// packages use the same compiled copy of Abseil to avoid a diamond dependency +// problem, which can occur if two packages built with different Abseil +// configuration settings are linked together. Diamond dependency problems in +// C++ may manifest as violations to the One Definition Rule (ODR) (resulting in +// linker errors), or undefined behavior (resulting in crashes). +// +// Diamond dependency problems can be avoided if all packages utilize the same +// exact version of Abseil. Building from source code with the same compilation +// parameters is the easiest way to avoid such dependency problems. However, for +// package managers who cannot control such compilation parameters, we are +// providing the file to allow you to inject ABI (Application Binary Interface) +// stability across builds. Settings options in this file will neither change +// API nor ABI, providing a stable copy of Abseil between packages. +// +// Care must be taken to keep options within these configurations isolated +// from any other dynamic settings, such as command-line flags which could alter +// these options. This file is provided specifically to help build and package +// managers provide a stable copy of Abseil within their libraries and binaries; +// other developers should not have need to alter the contents of this file. +// +// ----------------------------------------------------------------------------- +// Usage +// ----------------------------------------------------------------------------- +// +// For any particular package release, set the appropriate definitions within +// this file to whatever value makes the most sense for your package(s). Note +// that, by default, most of these options, at the moment, affect the +// implementation of types; future options may affect other implementation +// details. +// +// NOTE: the defaults within this file all assume that Abseil can select the +// proper Abseil implementation at compile-time, which will not be sufficient +// to guarantee ABI stability to package managers. +// +// ----------------------------------------------------------------------------- +// Type Compatibility Options +// ----------------------------------------------------------------------------- +// +// ABSL_OPTION_USE_STD_ANY +// +// This option controls whether absl::any is implemented as an alias to +// std::any, or as an independent implementation. +// +// A value of 0 means to use Abseil's implementation. This requires only C++11 +// support, and is expected to work on every toolchain we support. +// +// A value of 1 means to use an alias to std::any. This requires that all code +// using Abseil is built in C++17 mode or later. +// +// A value of 2 means to detect the C++ version being used to compile Abseil, +// and use an alias only if a working std::any is available. This option is +// useful when you are building your entire program, including all of its +// dependencies, from source. It should not be used otherwise -- for example, +// if you are distributing Abseil in a binary package manager -- since in +// mode 2, absl::any will name a different type, with a different mangled name +// and binary layout, depending on the compiler flags passed by the end user. +// For more info, see https://abseil.io/about/design/dropin-types. +// +// User code should not inspect this macro. To check in the preprocessor if +// absl::any is a typedef of std::any, use the feature macro ABSL_USES_STD_ANY. + +#define ABSL_OPTION_USE_STD_ANY 2 + + +// ABSL_OPTION_USE_STD_OPTIONAL +// +// This option controls whether absl::optional is implemented as an alias to +// std::optional, or as an independent implementation. +// +// A value of 0 means to use Abseil's implementation. This requires only C++11 +// support, and is expected to work on every toolchain we support. +// +// A value of 1 means to use an alias to std::optional. This requires that all +// code using Abseil is built in C++17 mode or later. +// +// A value of 2 means to detect the C++ version being used to compile Abseil, +// and use an alias only if a working std::optional is available. This option +// is useful when you are building your program from source. It should not be +// used otherwise -- for example, if you are distributing Abseil in a binary +// package manager -- since in mode 2, absl::optional will name a different +// type, with a different mangled name and binary layout, depending on the +// compiler flags passed by the end user. For more info, see +// https://abseil.io/about/design/dropin-types. + +// A value of 2 means to detect the C++ version being used to compile Abseil, +// and use an alias only if a working std::optional is available. This option +// should not be used when your program is not built from source -- for example, +// if you are distributing Abseil in a binary package manager -- since in mode +// 2, absl::optional will name a different template class, with a different +// mangled name and binary layout, depending on the compiler flags passed by the +// end user. +// +// User code should not inspect this macro. To check in the preprocessor if +// absl::optional is a typedef of std::optional, use the feature macro +// ABSL_USES_STD_OPTIONAL. + +#define ABSL_OPTION_USE_STD_OPTIONAL 2 + + +// ABSL_OPTION_USE_STD_STRING_VIEW +// +// This option controls whether absl::string_view is implemented as an alias to +// std::string_view, or as an independent implementation. +// +// A value of 0 means to use Abseil's implementation. This requires only C++11 +// support, and is expected to work on every toolchain we support. +// +// A value of 1 means to use an alias to std::string_view. This requires that +// all code using Abseil is built in C++17 mode or later. +// +// A value of 2 means to detect the C++ version being used to compile Abseil, +// and use an alias only if a working std::string_view is available. This +// option is useful when you are building your program from source. It should +// not be used otherwise -- for example, if you are distributing Abseil in a +// binary package manager -- since in mode 2, absl::string_view will name a +// different type, with a different mangled name and binary layout, depending on +// the compiler flags passed by the end user. For more info, see +// https://abseil.io/about/design/dropin-types. +// +// User code should not inspect this macro. To check in the preprocessor if +// absl::string_view is a typedef of std::string_view, use the feature macro +// ABSL_USES_STD_STRING_VIEW. + +#define ABSL_OPTION_USE_STD_STRING_VIEW 2 + + +// ABSL_OPTION_USE_STD_VARIANT +// +// This option controls whether absl::variant is implemented as an alias to +// std::variant, or as an independent implementation. +// +// A value of 0 means to use Abseil's implementation. This requires only C++11 +// support, and is expected to work on every toolchain we support. +// +// A value of 1 means to use an alias to std::variant. This requires that all +// code using Abseil is built in C++17 mode or later. +// +// A value of 2 means to detect the C++ version being used to compile Abseil, +// and use an alias only if a working std::variant is available. This option +// is useful when you are building your program from source. It should not be +// used otherwise -- for example, if you are distributing Abseil in a binary +// package manager -- since in mode 2, absl::variant will name a different +// type, with a different mangled name and binary layout, depending on the +// compiler flags passed by the end user. For more info, see +// https://abseil.io/about/design/dropin-types. +// +// User code should not inspect this macro. To check in the preprocessor if +// absl::variant is a typedef of std::variant, use the feature macro +// ABSL_USES_STD_VARIANT. + +#define ABSL_OPTION_USE_STD_VARIANT 2 + +#endif // ABSL_BASE_OPTIONS_H_ diff --git a/absl/container/flat_hash_map_test.cc b/absl/container/flat_hash_map_test.cc index 6cff1a25716..02d1f879dae 100644 --- a/absl/container/flat_hash_map_test.cc +++ b/absl/container/flat_hash_map_test.cc @@ -214,7 +214,7 @@ TEST(FlatHashMap, MergeExtractInsert) { EXPECT_THAT(m, UnorderedElementsAre(Pair(1, 17), Pair(2, 9))); } -#if (defined(ABSL_HAVE_STD_ANY) || !defined(_LIBCPP_VERSION)) && \ +#if (defined(ABSL_USES_STD_ANY) || !defined(_LIBCPP_VERSION)) && \ !defined(__EMSCRIPTEN__) TEST(FlatHashMap, Any) { absl::flat_hash_map m; @@ -246,7 +246,7 @@ TEST(FlatHashMap, Any) { ASSERT_NE(it2, m2.end()); EXPECT_EQ(7, it2->second); } -#endif // (defined(ABSL_HAVE_STD_ANY) || !defined(_LIBCPP_VERSION)) && +#endif // (defined(ABSL_USES_STD_ANY) || !defined(_LIBCPP_VERSION)) && // !defined(__EMSCRIPTEN__) } // namespace diff --git a/absl/container/internal/inlined_vector.h b/absl/container/internal/inlined_vector.h index 7d08f7e306e..f678c88dafd 100644 --- a/absl/container/internal/inlined_vector.h +++ b/absl/container/internal/inlined_vector.h @@ -471,12 +471,9 @@ auto Storage::Initialize(ValueAdapter values, size_type new_size) // safe to take on the allocation with size `0`. If `ConstructElements(...)` // throws, deallocation will be automatically handled by `~Storage()`. size_type new_capacity = ComputeCapacity(GetInlinedCapacity(), new_size); - pointer new_data = AllocatorTraits::allocate(*GetAllocPtr(), new_capacity); - - SetAllocatedData(new_data, new_capacity); + construct_data = AllocatorTraits::allocate(*GetAllocPtr(), new_capacity); + SetAllocatedData(construct_data, new_capacity); SetIsAllocated(); - - construct_data = new_data; } else { construct_data = GetInlinedData(); } @@ -503,9 +500,7 @@ auto Storage::Assign(ValueAdapter values, size_type new_size) -> void { if (new_size > storage_view.capacity) { size_type new_capacity = ComputeCapacity(storage_view.capacity, new_size); - pointer new_data = allocation_tx.Allocate(new_capacity); - - construct_loop = {new_data, new_size}; + construct_loop = {allocation_tx.Allocate(new_capacity), new_size}; destroy_loop = {storage_view.data, storage_view.size}; } else if (new_size > storage_view.size) { assign_loop = {storage_view.data, storage_view.size}; @@ -552,7 +547,6 @@ auto Storage::Resize(ValueAdapter values, size_type new_size) -> void { if (new_size > storage_view.capacity) { size_type new_capacity = ComputeCapacity(storage_view.capacity, new_size); pointer new_data = allocation_tx.Allocate(new_capacity); - construct_loop = {new_data + storage_view.size, new_size - storage_view.size}; move_construct_loop = {new_data, storage_view.size}; @@ -690,14 +684,14 @@ auto Storage::EmplaceBack(Args&&... args) -> reference { pointer construct_data; if (storage_view.size == storage_view.capacity) { size_type new_capacity = NextCapacity(storage_view.capacity); - pointer new_data = allocation_tx.Allocate(new_capacity); - - construct_data = new_data; + construct_data = allocation_tx.Allocate(new_capacity); } else { construct_data = storage_view.data; } - AllocatorTraits::construct(*GetAllocPtr(), construct_data + storage_view.size, + pointer last_ptr = construct_data + storage_view.size; + + AllocatorTraits::construct(*GetAllocPtr(), last_ptr, std::forward(args)...); if (allocation_tx.DidAllocate()) { @@ -707,8 +701,7 @@ auto Storage::EmplaceBack(Args&&... args) -> reference { storage_view.size); } ABSL_INTERNAL_CATCH_ANY { - AllocatorTraits::destroy(*GetAllocPtr(), - construct_data + storage_view.size); + AllocatorTraits::destroy(*GetAllocPtr(), last_ptr); ABSL_INTERNAL_RETHROW; } @@ -721,7 +714,7 @@ auto Storage::EmplaceBack(Args&&... args) -> reference { } AddSize(1); - return *(construct_data + storage_view.size); + return *last_ptr; } template @@ -793,9 +786,7 @@ auto Storage::ShrinkToFit() -> void { pointer construct_data; if (storage_view.size > GetInlinedCapacity()) { size_type new_capacity = storage_view.size; - pointer new_data = allocation_tx.Allocate(new_capacity); - - construct_data = new_data; + construct_data = allocation_tx.Allocate(new_capacity); } else { construct_data = GetInlinedData(); } diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index 9992ba4b8ac..9079a73e52a 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h @@ -668,7 +668,7 @@ class raw_hash_set { } ctrl_t* ctrl_ = nullptr; - // To avoid uninitialized member warnigs, put slot_ in an anonymous union. + // To avoid uninitialized member warnings, put slot_ in an anonymous union. // The member is not initialized on singleton and end iterators. union { slot_type* slot_; diff --git a/absl/meta/type_traits.h b/absl/meta/type_traits.h index 76959cdef60..d57a8792313 100644 --- a/absl/meta/type_traits.h +++ b/absl/meta/type_traits.h @@ -145,6 +145,18 @@ using IsMoveAssignableImpl = decltype(std::declval() = std::declval()); } // namespace type_traits_internal +// MSVC 19.20 has a regression that causes our workarounds to fail, but their +// std forms now appear to be compliant. +#if defined(_MSC_VER) && !defined(__clang__) && (_MSC_VER >= 1920) + +template +using is_copy_assignable = std::is_copy_assignable; + +template +using is_move_assignable = std::is_move_assignable; + +#else + template struct is_copy_assignable : type_traits_internal::is_detected< type_traits_internal::IsCopyAssignableImpl, T> { @@ -155,6 +167,8 @@ struct is_move_assignable : type_traits_internal::is_detected< type_traits_internal::IsMoveAssignableImpl, T> { }; +#endif + // void_t() // // Ignores the type of any its arguments and returns `void`. In general, this diff --git a/absl/random/internal/nonsecure_base_test.cc b/absl/random/internal/nonsecure_base_test.cc index d9de99019ce..698027fc6e0 100644 --- a/absl/random/internal/nonsecure_base_test.cc +++ b/absl/random/internal/nonsecure_base_test.cc @@ -154,9 +154,10 @@ TEST(NonsecureURBGBase, CompatibleWithDistributionUtils) { TEST(NonsecureURBGBase, CompatibleWithStdDistributions) { ExampleNonsecureURBG rbg; - std::uniform_int_distribution(0, 100)(rbg); - std::uniform_real_distribution()(rbg); - std::bernoulli_distribution(0.2)(rbg); + // Cast to void to suppress [[nodiscard]] warnings + static_cast(std::uniform_int_distribution(0, 100)(rbg)); + static_cast(std::uniform_real_distribution()(rbg)); + static_cast(std::bernoulli_distribution(0.2)(rbg)); } TEST(NonsecureURBGBase, ConsecutiveDefaultInstancesYieldUniqueVariates) { diff --git a/absl/random/log_uniform_int_distribution.h b/absl/random/log_uniform_int_distribution.h index 956a69070cc..de58bdbe14c 100644 --- a/absl/random/log_uniform_int_distribution.h +++ b/absl/random/log_uniform_int_distribution.h @@ -192,13 +192,15 @@ log_uniform_int_distribution::Generate( const double r = std::pow(p.base(), d); const double s = (r * p.base()) - 1.0; - base_e = (r > (std::numeric_limits::max)()) - ? (std::numeric_limits::max)() - : static_cast(r); - - top_e = (s > (std::numeric_limits::max)()) - ? (std::numeric_limits::max)() - : static_cast(s); + base_e = + (r > static_cast((std::numeric_limits::max)())) + ? (std::numeric_limits::max)() + : static_cast(r); + + top_e = + (s > static_cast((std::numeric_limits::max)())) + ? (std::numeric_limits::max)() + : static_cast(s); } const unsigned_type lo = (base_e >= p.range()) ? p.range() : base_e; diff --git a/absl/strings/numbers.h b/absl/strings/numbers.h index 745de67ad86..9b8ec89a894 100644 --- a/absl/strings/numbers.h +++ b/absl/strings/numbers.h @@ -37,7 +37,14 @@ #include #include "absl/base/internal/bits.h" +#ifdef __SSE4_2__ +// TODO(jorg): Remove this when we figure out the right way +// to swap bytes on SSE 4.2 that works with the compilers +// we claim to support. Also, add tests for the compiler +// that doesn't support the Intel _bswap64 intrinsic but +// does support all the SSE 4.2 intrinsics #include "absl/base/internal/endian.h" +#endif #include "absl/base/macros.h" #include "absl/base/port.h" #include "absl/numeric/int128.h" @@ -189,12 +196,12 @@ ABSL_MUST_USE_RESULT bool safe_strtoi_base(absl::string_view s, int_type* out, // Returns the number of non-pad digits of the output (it can never be zero // since 0 has one digit). inline size_t FastHexToBufferZeroPad16(uint64_t val, char* out) { - uint64_t be = absl::big_endian::FromHost64(val); #ifdef __SSE4_2__ + uint64_t be = absl::big_endian::FromHost64(val); const auto kNibbleMask = _mm_set1_epi8(0xf); const auto kHexDigits = _mm_setr_epi8('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'); - auto v = _mm_loadu_si64(reinterpret_cast<__m128i*>(&be)); // load lo dword + auto v = _mm_loadl_epi64(reinterpret_cast<__m128i*>(&be)); // load lo dword auto v4 = _mm_srli_epi64(v, 4); // shift 4 right auto il = _mm_unpacklo_epi8(v4, v); // interleave bytes auto m = _mm_and_si128(il, kNibbleMask); // mask out nibbles @@ -202,7 +209,7 @@ inline size_t FastHexToBufferZeroPad16(uint64_t val, char* out) { _mm_storeu_si128(reinterpret_cast<__m128i*>(out), hexchars); #else for (int i = 0; i < 8; ++i) { - auto byte = (be >> (8 * i)) & 0xFF; + auto byte = (val >> (56 - 8 * i)) & 0xFF; auto* hex = &absl::numbers_internal::kHexTable[byte * 2]; std::memcpy(out + 2 * i, hex, 2); } diff --git a/absl/strings/str_cat.cc b/absl/strings/str_cat.cc index d587789972e..4bd56f5f438 100644 --- a/absl/strings/str_cat.cc +++ b/absl/strings/str_cat.cc @@ -27,23 +27,21 @@ namespace absl { AlphaNum::AlphaNum(Hex hex) { + static_assert(numbers_internal::kFastToBufferSize >= 32, + "This function only works when output buffer >= 32 bytes long"); char* const end = &digits_[numbers_internal::kFastToBufferSize]; - char* writer = end; - uint64_t value = hex.value; - do { - *--writer = absl::numbers_internal::kHexChar[value & 0xF]; - value >>= 4; - } while (value != 0); - - char* beg; - if (end - writer < hex.width) { - beg = end - hex.width; - std::fill_n(beg, writer - beg, hex.fill); + auto real_width = + absl::numbers_internal::FastHexToBufferZeroPad16(hex.value, end - 16); + if (real_width >= hex.width) { + piece_ = absl::string_view(end - real_width, real_width); } else { - beg = writer; + // Pad first 16 chars because FastHexToBufferZeroPad16 pads only to 16 and + // max pad width can be up to 20. + std::memset(end - 32, hex.fill, 16); + // Patch up everything else up to the real_width. + std::memset(end - real_width - 16, hex.fill, 16); + piece_ = absl::string_view(end - hex.width, hex.width); } - - piece_ = absl::string_view(beg, end - beg); } AlphaNum::AlphaNum(Dec dec) { diff --git a/absl/strings/str_format.h b/absl/strings/str_format.h index 607e2bcaae1..c11c93a2483 100644 --- a/absl/strings/str_format.h +++ b/absl/strings/str_format.h @@ -400,6 +400,12 @@ int FPrintF(std::FILE* output, const FormatSpec& format, // This function is functionally equivalent to `std::snprintf()` (and // type-safe); prefer `absl::SNPrintF()` over `std::snprintf()`. // +// In particular, a successful call to `absl::SNPrintF()` writes at most `size` +// bytes of the formatted output to `output`, including a null terminator, and +// returns the number of bytes that would have been written if truncation did +// not occur. In the event of an error, a negative value is returned and `errno` +// is set. +// // Example: // // std::string_view s = "Ulaanbaatar"; diff --git a/absl/strings/string_view.cc b/absl/strings/string_view.cc index dc034a83eea..d5e1a3de9ff 100644 --- a/absl/strings/string_view.cc +++ b/absl/strings/string_view.cc @@ -14,7 +14,7 @@ #include "absl/strings/string_view.h" -#ifndef ABSL_HAVE_STD_STRING_VIEW +#ifndef ABSL_USES_STD_STRING_VIEW #include #include @@ -230,4 +230,4 @@ constexpr string_view::size_type string_view::kMaxSize; } // namespace absl -#endif // ABSL_HAVE_STD_STRING_VIEW +#endif // ABSL_USES_STD_STRING_VIEW diff --git a/absl/strings/string_view.h b/absl/strings/string_view.h index 3438ccc12a7..07075e80ec6 100644 --- a/absl/strings/string_view.h +++ b/absl/strings/string_view.h @@ -30,7 +30,7 @@ #include #include "absl/base/config.h" -#ifdef ABSL_HAVE_STD_STRING_VIEW +#ifdef ABSL_USES_STD_STRING_VIEW #include // IWYU pragma: export @@ -38,7 +38,7 @@ namespace absl { using std::string_view; } // namespace absl -#else // ABSL_HAVE_STD_STRING_VIEW +#else // ABSL_USES_STD_STRING_VIEW #if ABSL_HAVE_BUILTIN(__builtin_memcmp) || \ (defined(__GNUC__) && !defined(__clang__)) @@ -580,7 +580,7 @@ std::ostream& operator<<(std::ostream& o, string_view piece); #undef ABSL_INTERNAL_STRING_VIEW_MEMCMP -#endif // ABSL_HAVE_STD_STRING_VIEW +#endif // ABSL_USES_STD_STRING_VIEW namespace absl { diff --git a/absl/strings/string_view_test.cc b/absl/strings/string_view_test.cc index 86f2fbcd003..96dacdf048c 100644 --- a/absl/strings/string_view_test.cc +++ b/absl/strings/string_view_test.cc @@ -830,7 +830,7 @@ TEST(StringViewTest, FrontBackSingleChar) { // to the standard, but `absl::string_view` implements a different // behavior for historical reasons. We work around tests that construct // `string_view` from `nullptr` when using libc++. -#if !defined(ABSL_HAVE_STD_STRING_VIEW) || \ +#if !defined(ABSL_USES_STD_STRING_VIEW) || \ (!(defined(_GLIBCXX_RELEASE) && _GLIBCXX_RELEASE >= 9) && \ !defined(_LIBCPP_VERSION) && !defined(_MSC_VER)) #define ABSL_HAVE_STRING_VIEW_FROM_NULLPTR 1 @@ -938,7 +938,7 @@ TEST(StringViewTest, ConstexprCompiles) { #endif constexpr absl::string_view cstr_len("cstr", 4); -#if defined(ABSL_HAVE_STD_STRING_VIEW) +#if defined(ABSL_USES_STD_STRING_VIEW) // In libstdc++ (as of 7.2), `std::string_view::string_view(const char*)` // calls `std::char_traits::length(const char*)` to get the std::string // length, but it is not marked constexpr yet. See GCC bug: @@ -952,7 +952,7 @@ TEST(StringViewTest, ConstexprCompiles) { #define ABSL_HAVE_CONSTEXPR_STRING_VIEW_FROM_CSTR 1 #endif // !__GLIBCXX__ -#else // ABSL_HAVE_STD_STRING_VIEW +#else // ABSL_USES_STD_STRING_VIEW // This duplicates the check for __builtin_strlen in the header. #if ABSL_HAVE_BUILTIN(__builtin_strlen) || \ @@ -967,7 +967,7 @@ TEST(StringViewTest, ConstexprCompiles) { #define ABSL_HAVE_CONSTEXPR_STRING_VIEW_FROM_CSTR 1 #endif -#endif // ABSL_HAVE_STD_STRING_VIEW +#endif // ABSL_USES_STD_STRING_VIEW #ifdef ABSL_HAVE_CONSTEXPR_STRING_VIEW_FROM_CSTR constexpr absl::string_view cstr_strlen("foo"); @@ -1136,7 +1136,7 @@ TEST(HugeStringView, TwoPointTwoGB) { } #endif // THREAD_SANITIZER -#if !defined(NDEBUG) && !defined(ABSL_HAVE_STD_STRING_VIEW) +#if !defined(NDEBUG) && !defined(ABSL_USES_STD_STRING_VIEW) TEST(NonNegativeLenTest, NonNegativeLen) { ABSL_EXPECT_DEATH_IF_SUPPORTED(absl::string_view("xyz", -1), "len <= kMaxSize"); @@ -1152,7 +1152,7 @@ TEST(LenExceedsMaxSizeTest, LenExceedsMaxSize) { ABSL_EXPECT_DEATH_IF_SUPPORTED(absl::string_view("", max_size + 1), "len <= kMaxSize"); } -#endif // !defined(NDEBUG) && !defined(ABSL_HAVE_STD_STRING_VIEW) +#endif // !defined(NDEBUG) && !defined(ABSL_USES_STD_STRING_VIEW) class StringViewStreamTest : public ::testing::Test { public: diff --git a/absl/synchronization/internal/waiter.cc b/absl/synchronization/internal/waiter.cc index d83d8087f8d..2f41aa5f2b4 100644 --- a/absl/synchronization/internal/waiter.cc +++ b/absl/synchronization/internal/waiter.cc @@ -161,7 +161,7 @@ bool Waiter::Wait(KernelTimeout t) { void Waiter::Post() { if (futex_.fetch_add(1, std::memory_order_release) == 0) { - // We incremented from 0, need to wake a potential waker. + // We incremented from 0, need to wake a potential waiter. Poke(); } } @@ -210,8 +210,8 @@ void Waiter::Init() { ABSL_RAW_LOG(FATAL, "pthread_cond_init failed: %d", err2); } - waiter_count_.store(0, std::memory_order_relaxed); - wakeup_count_.store(0, std::memory_order_relaxed); + waiter_count_ = 0; + wakeup_count_ = 0; } bool Waiter::Wait(KernelTimeout t) { @@ -221,24 +221,12 @@ bool Waiter::Wait(KernelTimeout t) { } PthreadMutexHolder h(&mu_); - waiter_count_.fetch_add(1, std::memory_order_relaxed); + ++waiter_count_; // Loop until we find a wakeup to consume or timeout. // Note that, since the thread ticker is just reset, we don't need to check // whether the thread is idle on the very first pass of the loop. bool first_pass = true; - while (true) { - int x = wakeup_count_.load(std::memory_order_relaxed); - if (x != 0) { - if (!wakeup_count_.compare_exchange_weak(x, x - 1, - std::memory_order_acquire, - std::memory_order_relaxed)) { - continue; // Raced with someone, retry. - } - // Successfully consumed a wakeup, we're done. - waiter_count_.fetch_sub(1, std::memory_order_relaxed); - return true; - } - + while (wakeup_count_ == 0) { if (!first_pass) MaybeBecomeIdle(); // No wakeups available, time to wait. if (!t.has_timeout()) { @@ -249,34 +237,38 @@ bool Waiter::Wait(KernelTimeout t) { } else { const int err = pthread_cond_timedwait(&cv_, &mu_, &abs_timeout); if (err == ETIMEDOUT) { - waiter_count_.fetch_sub(1, std::memory_order_relaxed); + --waiter_count_; return false; } if (err != 0) { - ABSL_RAW_LOG(FATAL, "pthread_cond_wait failed: %d", err); + ABSL_RAW_LOG(FATAL, "pthread_cond_timedwait failed: %d", err); } } first_pass = false; } + // Consume a wakeup and we're done. + --wakeup_count_; + --waiter_count_; + return true; } void Waiter::Post() { - wakeup_count_.fetch_add(1, std::memory_order_release); - Poke(); + PthreadMutexHolder h(&mu_); + ++wakeup_count_; + InternalCondVarPoke(); } void Waiter::Poke() { - if (waiter_count_.load(std::memory_order_relaxed) == 0) { - return; - } - // Potentially a waker. Take the lock and check again. PthreadMutexHolder h(&mu_); - if (waiter_count_.load(std::memory_order_relaxed) == 0) { - return; - } - const int err = pthread_cond_signal(&cv_); - if (err != 0) { - ABSL_RAW_LOG(FATAL, "pthread_cond_signal failed: %d", err); + InternalCondVarPoke(); +} + +void Waiter::InternalCondVarPoke() { + if (waiter_count_ != 0) { + const int err = pthread_cond_signal(&cv_); + if (ABSL_PREDICT_FALSE(err != 0)) { + ABSL_RAW_LOG(FATAL, "pthread_cond_signal failed: %d", err); + } } } @@ -456,7 +448,7 @@ void Waiter::Poke() { if (waiter_count_.load(std::memory_order_relaxed) == 0) { return; } - // Potentially a waker. Take the lock and check again. + // Potentially a waiter. Take the lock and check again. LockHolder h(WinHelper::GetLock(this)); if (waiter_count_.load(std::memory_order_relaxed) == 0) { return; diff --git a/absl/synchronization/internal/waiter.h b/absl/synchronization/internal/waiter.h index 099935453be..c23897007e7 100644 --- a/absl/synchronization/internal/waiter.h +++ b/absl/synchronization/internal/waiter.h @@ -106,10 +106,13 @@ class Waiter { static_assert(sizeof(int32_t) == sizeof(futex_), "Wrong size for futex"); #elif ABSL_WAITER_MODE == ABSL_WAITER_MODE_CONDVAR + // REQUIRES: mu_ must be held. + void InternalCondVarPoke(); + pthread_mutex_t mu_; pthread_cond_t cv_; - std::atomic waiter_count_; - std::atomic wakeup_count_; // Unclaimed wakeups, written under lock. + int waiter_count_; + int wakeup_count_; // Unclaimed wakeups. #elif ABSL_WAITER_MODE == ABSL_WAITER_MODE_SEM sem_t sem_; diff --git a/absl/synchronization/lifetime_test.cc b/absl/synchronization/lifetime_test.cc index 34b8875b4b7..cc973a32907 100644 --- a/absl/synchronization/lifetime_test.cc +++ b/absl/synchronization/lifetime_test.cc @@ -122,6 +122,11 @@ class OnDestruction { Function fn_; }; +// These tests require that the compiler correctly supports C++11 constant +// initialization... but MSVC has a known regression since v19.10: +// https://developercommunity.visualstudio.com/content/problem/336946/class-with-constexpr-constructor-not-using-static.html +// TODO(epastor): Limit the affected range once MSVC fixes this bug. +#if defined(__clang__) || !(defined(_MSC_VER) && _MSC_VER > 1900) // kConstInit // Test early usage. (Declaration comes first; definitions must appear after // the test runner.) @@ -151,6 +156,7 @@ OnConstruction check_still_locked([]() ABSL_NO_THREAD_SAFETY_ANALYSIS { const_init_sanity_mutex.AssertHeld(); const_init_sanity_mutex.Unlock(); }); +#endif // defined(__clang__) || !(defined(_MSC_VER) && _MSC_VER > 1900) // Test shutdown usage. (Declarations come first; definitions must appear after // the test runner.) diff --git a/absl/time/civil_time.h b/absl/time/civil_time.h index c8166d07c94..77c3be2eba6 100644 --- a/absl/time/civil_time.h +++ b/absl/time/civil_time.h @@ -519,7 +519,7 @@ namespace time_internal { // For functions found via ADL on civil-time tags. // // Example: // -// absl::CivilDay d = absl::CivilDay("1969-07-20"); +// absl::CivilDay d = absl::CivilDay(1969, 7, 20); // std::cout << "Date is: " << d << "\n"; // std::ostream& operator<<(std::ostream& os, CivilYear y); diff --git a/absl/time/duration.cc b/absl/time/duration.cc index 9a8768113f3..f0b4631d6fa 100644 --- a/absl/time/duration.cc +++ b/absl/time/duration.cc @@ -197,11 +197,11 @@ inline int64_t DecodeTwosComp(uint64_t v) { return absl::bit_cast(v); } // double as overflow cases. inline bool SafeAddRepHi(double a_hi, double b_hi, Duration* d) { double c = a_hi + b_hi; - if (c >= kint64max) { + if (c >= static_cast(kint64max)) { *d = InfiniteDuration(); return false; } - if (c <= kint64min) { + if (c <= static_cast(kint64min)) { *d = -InfiniteDuration(); return false; } diff --git a/absl/time/time.h b/absl/time/time.h index 0b7312ee983..46ac26b3c0d 100644 --- a/absl/time/time.h +++ b/absl/time/time.h @@ -422,7 +422,9 @@ Duration Milliseconds(T n) { template = 0> Duration Seconds(T n) { if (n >= 0) { // Note: `NaN >= 0` is false. - if (n >= (std::numeric_limits::max)()) return InfiniteDuration(); + if (n >= static_cast((std::numeric_limits::max)())) { + return InfiniteDuration(); + } return time_internal::MakePosDoubleDuration(n); } else { if (std::isnan(n)) diff --git a/absl/types/any.h b/absl/types/any.h index 507c8b2cc4f..f79676949d3 100644 --- a/absl/types/any.h +++ b/absl/types/any.h @@ -56,7 +56,7 @@ #include "absl/base/config.h" #include "absl/utility/utility.h" -#ifdef ABSL_HAVE_STD_ANY +#ifdef ABSL_USES_STD_ANY #include // IWYU pragma: export @@ -67,7 +67,7 @@ using std::bad_any_cast; using std::make_any; } // namespace absl -#else // ABSL_HAVE_STD_ANY +#else // ABSL_USES_STD_ANY #include #include @@ -538,6 +538,6 @@ T* any_cast(any* operand) noexcept { #undef ABSL_ANY_DETAIL_HAS_RTTI -#endif // ABSL_HAVE_STD_ANY +#endif // ABSL_USES_STD_ANY #endif // ABSL_TYPES_ANY_H_ diff --git a/absl/types/any_exception_safety_test.cc b/absl/types/any_exception_safety_test.cc index 17d7f5d7781..31c1140135f 100644 --- a/absl/types/any_exception_safety_test.cc +++ b/absl/types/any_exception_safety_test.cc @@ -18,7 +18,7 @@ // This test is a no-op when absl::any is an alias for std::any and when // exceptions are not enabled. -#if !defined(ABSL_HAVE_STD_ANY) && defined(ABSL_HAVE_EXCEPTIONS) +#if !defined(ABSL_USES_STD_ANY) && defined(ABSL_HAVE_EXCEPTIONS) #include #include @@ -170,4 +170,4 @@ TEST(AnyExceptionSafety, Emplace) { } // namespace -#endif // #if !defined(ABSL_HAVE_STD_ANY) && defined(ABSL_HAVE_EXCEPTIONS) +#endif // #if !defined(ABSL_USES_STD_ANY) && defined(ABSL_HAVE_EXCEPTIONS) diff --git a/absl/types/any_test.cc b/absl/types/any_test.cc index 4a848ae4ae8..70e4ba22b12 100644 --- a/absl/types/any_test.cc +++ b/absl/types/any_test.cc @@ -15,7 +15,7 @@ #include "absl/types/any.h" // This test is a no-op when absl::any is an alias for std::any. -#if !defined(ABSL_HAVE_STD_ANY) +#if !defined(ABSL_USES_STD_ANY) #include #include @@ -642,7 +642,7 @@ TEST(AnyTest, ConversionConstructionCausesOneCopy) { // Tests for Exception Behavior // ////////////////////////////////// -#if defined(ABSL_HAVE_STD_ANY) +#if defined(ABSL_USES_STD_ANY) // If using a std `any` implementation, we can't check for a specific message. #define ABSL_ANY_TEST_EXPECT_BAD_ANY_CAST(...) \ @@ -656,7 +656,7 @@ TEST(AnyTest, ConversionConstructionCausesOneCopy) { ABSL_BASE_INTERNAL_EXPECT_FAIL((__VA_ARGS__), absl::bad_any_cast, \ "Bad any cast") -#endif // defined(ABSL_HAVE_STD_ANY) +#endif // defined(ABSL_USES_STD_ANY) TEST(AnyTest, ThrowBadAlloc) { { @@ -764,7 +764,7 @@ TEST(AnyTest, FailedEmplace) { BadCopyable bad; absl::any target(absl::in_place_type); ABSL_ANY_TEST_EXPECT_BAD_COPY(target.emplace(bad)); -#if defined(ABSL_HAVE_STD_ANY) && defined(__GLIBCXX__) +#if defined(ABSL_USES_STD_ANY) && defined(__GLIBCXX__) // libstdc++ std::any::emplace() implementation (as of 7.2) has a bug: if an // exception is thrown, *this contains a value. #define ABSL_GLIBCXX_ANY_EMPLACE_EXCEPTION_BUG 1 @@ -778,4 +778,4 @@ TEST(AnyTest, FailedEmplace) { } // namespace -#endif // #if !defined(ABSL_HAVE_STD_ANY) +#endif // #if !defined(ABSL_USES_STD_ANY) diff --git a/absl/types/bad_any_cast.cc b/absl/types/bad_any_cast.cc index 505919a5583..2a538126645 100644 --- a/absl/types/bad_any_cast.cc +++ b/absl/types/bad_any_cast.cc @@ -14,7 +14,7 @@ #include "absl/types/bad_any_cast.h" -#ifndef ABSL_HAVE_STD_ANY +#ifndef ABSL_USES_STD_ANY #include @@ -41,4 +41,4 @@ void ThrowBadAnyCast() { } // namespace any_internal } // namespace absl -#endif // ABSL_HAVE_STD_ANY +#endif // ABSL_USES_STD_ANY diff --git a/absl/types/bad_any_cast.h b/absl/types/bad_any_cast.h index 8d020ede466..6a53c010810 100644 --- a/absl/types/bad_any_cast.h +++ b/absl/types/bad_any_cast.h @@ -25,7 +25,7 @@ #include "absl/base/config.h" -#ifdef ABSL_HAVE_STD_ANY +#ifdef ABSL_USES_STD_ANY #include @@ -33,7 +33,7 @@ namespace absl { using std::bad_any_cast; } // namespace absl -#else // ABSL_HAVE_STD_ANY +#else // ABSL_USES_STD_ANY namespace absl { @@ -66,6 +66,6 @@ namespace any_internal { } // namespace any_internal } // namespace absl -#endif // ABSL_HAVE_STD_ANY +#endif // ABSL_USES_STD_ANY #endif // ABSL_TYPES_BAD_ANY_CAST_H_ diff --git a/absl/types/bad_optional_access.cc b/absl/types/bad_optional_access.cc index a791c7c21da..d9ec21bf42b 100644 --- a/absl/types/bad_optional_access.cc +++ b/absl/types/bad_optional_access.cc @@ -14,7 +14,7 @@ #include "absl/types/bad_optional_access.h" -#ifndef ABSL_HAVE_STD_OPTIONAL +#ifndef ABSL_USES_STD_OPTIONAL #include @@ -43,4 +43,4 @@ void throw_bad_optional_access() { } // namespace optional_internal } // namespace absl -#endif // ABSL_HAVE_STD_OPTIONAL +#endif // ABSL_USES_STD_OPTIONAL diff --git a/absl/types/bad_optional_access.h b/absl/types/bad_optional_access.h index add5c452cd5..32dd6a91ea6 100644 --- a/absl/types/bad_optional_access.h +++ b/absl/types/bad_optional_access.h @@ -25,7 +25,7 @@ #include "absl/base/config.h" -#ifdef ABSL_HAVE_STD_OPTIONAL +#ifdef ABSL_USES_STD_OPTIONAL #include @@ -33,7 +33,7 @@ namespace absl { using std::bad_optional_access; } // namespace absl -#else // ABSL_HAVE_STD_OPTIONAL +#else // ABSL_USES_STD_OPTIONAL namespace absl { @@ -69,6 +69,6 @@ namespace optional_internal { } // namespace optional_internal } // namespace absl -#endif // ABSL_HAVE_STD_OPTIONAL +#endif // ABSL_USES_STD_OPTIONAL #endif // ABSL_TYPES_BAD_OPTIONAL_ACCESS_H_ diff --git a/absl/types/bad_variant_access.cc b/absl/types/bad_variant_access.cc index a4325c8df7d..dbaea9e9d1d 100644 --- a/absl/types/bad_variant_access.cc +++ b/absl/types/bad_variant_access.cc @@ -14,7 +14,7 @@ #include "absl/types/bad_variant_access.h" -#ifndef ABSL_HAVE_STD_VARIANT +#ifndef ABSL_USES_STD_VARIANT #include #include @@ -59,4 +59,4 @@ void Rethrow() { } // namespace variant_internal } // namespace absl -#endif // ABSL_HAVE_STD_VARIANT +#endif // ABSL_USES_STD_VARIANT diff --git a/absl/types/bad_variant_access.h b/absl/types/bad_variant_access.h index 637db435505..6935d01b99f 100644 --- a/absl/types/bad_variant_access.h +++ b/absl/types/bad_variant_access.h @@ -25,7 +25,7 @@ #include "absl/base/config.h" -#ifdef ABSL_HAVE_STD_VARIANT +#ifdef ABSL_USES_STD_VARIANT #include @@ -33,7 +33,7 @@ namespace absl { using std::bad_variant_access; } // namespace absl -#else // ABSL_HAVE_STD_VARIANT +#else // ABSL_USES_STD_VARIANT namespace absl { @@ -73,6 +73,6 @@ namespace variant_internal { } // namespace variant_internal } // namespace absl -#endif // ABSL_HAVE_STD_VARIANT +#endif // ABSL_USES_STD_VARIANT #endif // ABSL_TYPES_BAD_VARIANT_ACCESS_H_ diff --git a/absl/types/internal/variant.h b/absl/types/internal/variant.h index 19de2e13759..58b385921f5 100644 --- a/absl/types/internal/variant.h +++ b/absl/types/internal/variant.h @@ -37,7 +37,7 @@ #include "absl/types/bad_variant_access.h" #include "absl/utility/utility.h" -#if !defined(ABSL_HAVE_STD_VARIANT) +#if !defined(ABSL_USES_STD_VARIANT) namespace absl { @@ -1640,5 +1640,5 @@ struct VariantHashBase // IWYU pragma: export @@ -50,7 +50,7 @@ using std::nullopt_t; using std::nullopt; } // namespace absl -#else // ABSL_HAVE_STD_OPTIONAL +#else // ABSL_USES_STD_OPTIONAL #include #include @@ -767,6 +767,6 @@ struct hash > #undef ABSL_MSVC_CONSTEXPR_BUG_IN_UNION_LIKE_CLASS -#endif // ABSL_HAVE_STD_OPTIONAL +#endif // ABSL_USES_STD_OPTIONAL #endif // ABSL_TYPES_OPTIONAL_H_ diff --git a/absl/types/optional_exception_safety_test.cc b/absl/types/optional_exception_safety_test.cc index f99e35c0544..0f7fae6cb4c 100644 --- a/absl/types/optional_exception_safety_test.cc +++ b/absl/types/optional_exception_safety_test.cc @@ -18,7 +18,7 @@ // This test is a no-op when absl::optional is an alias for std::optional and // when exceptions are not enabled. -#if !defined(ABSL_HAVE_STD_OPTIONAL) && defined(ABSL_HAVE_EXCEPTIONS) +#if !defined(ABSL_USES_STD_OPTIONAL) && defined(ABSL_HAVE_EXCEPTIONS) #include "gtest/gtest.h" #include "absl/base/internal/exception_safety_testing.h" @@ -287,4 +287,4 @@ TEST(OptionalExceptionSafety, NothrowMoveAssign) { } // namespace absl -#endif // #if !defined(ABSL_HAVE_STD_OPTIONAL) && defined(ABSL_HAVE_EXCEPTIONS) +#endif // #if !defined(ABSL_USES_STD_OPTIONAL) && defined(ABSL_HAVE_EXCEPTIONS) diff --git a/absl/types/optional_test.cc b/absl/types/optional_test.cc index 35efa863507..47d5c8a29e0 100644 --- a/absl/types/optional_test.cc +++ b/absl/types/optional_test.cc @@ -15,7 +15,7 @@ #include "absl/types/optional.h" // This test is a no-op when absl::optional is an alias for std::optional. -#if !defined(ABSL_HAVE_STD_OPTIONAL) +#if !defined(ABSL_USES_STD_OPTIONAL) #include #include @@ -224,7 +224,7 @@ TEST(optionalTest, CopyConstructor) { EXPECT_FALSE( absl::is_trivially_copy_constructible>::value); -#if defined(ABSL_HAVE_STD_OPTIONAL) && defined(__GLIBCXX__) +#if defined(ABSL_USES_STD_OPTIONAL) && defined(__GLIBCXX__) // libstdc++ std::optional implementation (as of 7.2) has a bug: when T is // trivially copyable, optional is not trivially copyable (due to one of // its base class is unconditionally nontrivial). @@ -279,7 +279,7 @@ TEST(optionalTest, CopyConstructor) { // std::optional when T is volatile-qualified. So skipping this test. // Bug report: // https://connect.microsoft.com/VisualStudio/feedback/details/3142534 -#if defined(ABSL_HAVE_STD_OPTIONAL) && defined(_MSC_VER) && _MSC_VER >= 1911 +#if defined(ABSL_USES_STD_OPTIONAL) && defined(_MSC_VER) && _MSC_VER >= 1911 #define ABSL_MSVC_OPTIONAL_VOLATILE_COPY_BUG 1 #endif #ifndef ABSL_MSVC_OPTIONAL_VOLATILE_COPY_BUG @@ -305,7 +305,7 @@ TEST(optionalTest, MoveConstructor) { EXPECT_FALSE(std::is_move_constructible>::value); // test noexcept EXPECT_TRUE(std::is_nothrow_move_constructible>::value); -#ifndef ABSL_HAVE_STD_OPTIONAL +#ifndef ABSL_USES_STD_OPTIONAL EXPECT_EQ( absl::default_allocator_is_nothrow::value, std::is_nothrow_move_constructible>::value); @@ -639,7 +639,7 @@ TEST(optionalTest, CopyAssignment) { EXPECT_FALSE(absl::is_trivially_copy_assignable::value); // std::optional doesn't support volatile nontrivial types. -#ifndef ABSL_HAVE_STD_OPTIONAL +#ifndef ABSL_USES_STD_OPTIONAL { StructorListener listener; Listenable::listener = &listener; @@ -658,7 +658,7 @@ TEST(optionalTest, CopyAssignment) { EXPECT_EQ(1, listener.destruct); EXPECT_EQ(1, listener.volatile_copy_assign); } -#endif // ABSL_HAVE_STD_OPTIONAL +#endif // ABSL_USES_STD_OPTIONAL } TEST(optionalTest, MoveAssignment) { @@ -682,7 +682,7 @@ TEST(optionalTest, MoveAssignment) { EXPECT_EQ(1, listener.move_assign); } // std::optional doesn't support volatile nontrivial types. -#ifndef ABSL_HAVE_STD_OPTIONAL +#ifndef ABSL_USES_STD_OPTIONAL { StructorListener listener; Listenable::listener = &listener; @@ -702,7 +702,7 @@ TEST(optionalTest, MoveAssignment) { EXPECT_EQ(1, listener.destruct); EXPECT_EQ(1, listener.volatile_move_assign); } -#endif // ABSL_HAVE_STD_OPTIONAL +#endif // ABSL_USES_STD_OPTIONAL EXPECT_FALSE(absl::is_move_assignable>::value); EXPECT_TRUE(absl::is_move_assignable>::value); EXPECT_TRUE(absl::is_move_assignable>::value); @@ -1560,7 +1560,7 @@ TEST(optionalTest, NoExcept) { static_assert( std::is_nothrow_move_constructible>::value, ""); -#ifndef ABSL_HAVE_STD_OPTIONAL +#ifndef ABSL_USES_STD_OPTIONAL static_assert(absl::default_allocator_is_nothrow::value == std::is_nothrow_move_constructible< absl::optional>::value, @@ -1658,4 +1658,4 @@ TEST(optionalTest, InPlaceTSFINAEBug) { } // namespace -#endif // #if !defined(ABSL_HAVE_STD_OPTIONAL) +#endif // #if !defined(ABSL_USES_STD_OPTIONAL) diff --git a/absl/types/variant.h b/absl/types/variant.h index 7ef7f88ca11..f3558703a82 100644 --- a/absl/types/variant.h +++ b/absl/types/variant.h @@ -45,7 +45,7 @@ #include "absl/base/config.h" #include "absl/utility/utility.h" -#ifdef ABSL_HAVE_STD_VARIANT +#ifdef ABSL_USES_STD_VARIANT #include // IWYU pragma: export @@ -64,7 +64,7 @@ using std::variant_size_v; using std::visit; } // namespace absl -#else // ABSL_HAVE_STD_VARIANT +#else // ABSL_USES_STD_VARIANT #include #include @@ -812,7 +812,7 @@ struct hash> } // namespace std -#endif // ABSL_HAVE_STD_VARIANT +#endif // ABSL_USES_STD_VARIANT namespace absl { namespace variant_internal { diff --git a/absl/types/variant_exception_safety_test.cc b/absl/types/variant_exception_safety_test.cc index fd7e6c7fe80..b486a71e5b7 100644 --- a/absl/types/variant_exception_safety_test.cc +++ b/absl/types/variant_exception_safety_test.cc @@ -18,7 +18,7 @@ // This test is a no-op when absl::variant is an alias for std::variant and when // exceptions are not enabled. -#if !defined(ABSL_HAVE_STD_VARIANT) && defined(ABSL_HAVE_EXCEPTIONS) +#if !defined(ABSL_USES_STD_VARIANT) && defined(ABSL_HAVE_EXCEPTIONS) #include #include @@ -237,7 +237,7 @@ TEST(VariantExceptionSafetyTest, CopyAssign) { } // libstdc++ std::variant has bugs on copy assignment regarding exception // safety. -#if !(defined(ABSL_HAVE_STD_VARIANT) && defined(__GLIBCXX__)) +#if !(defined(ABSL_USES_STD_VARIANT) && defined(__GLIBCXX__)) // index() != j // if is_nothrow_copy_constructible_v or // !is_nothrow_move_constructible is true, equivalent to @@ -268,7 +268,7 @@ TEST(VariantExceptionSafetyTest, CopyAssign) { .Test()); EXPECT_FALSE(tester.WithContracts(strong_guarantee).Test()); } -#endif // !(defined(ABSL_HAVE_STD_VARIANT) && defined(__GLIBCXX__)) +#endif // !(defined(ABSL_USES_STD_VARIANT) && defined(__GLIBCXX__)) { // is_nothrow_copy_constructible_v == false && // is_nothrow_move_constructible_v == true @@ -325,7 +325,7 @@ TEST(VariantExceptionSafetyTest, MoveAssign) { // The fix is targeted for gcc-9. // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87431#c7 // https://gcc.gnu.org/viewcvs/gcc?view=revision&revision=267614 -#if !(defined(ABSL_HAVE_STD_VARIANT) && \ +#if !(defined(ABSL_USES_STD_VARIANT) && \ defined(_GLIBCXX_RELEASE) && _GLIBCXX_RELEASE == 8) // - otherwise (index() != j), equivalent to // emplace(get(std::move(rhs))) @@ -342,7 +342,7 @@ TEST(VariantExceptionSafetyTest, MoveAssign) { auto copy = rhs; *lhs = std::move(copy); })); -#endif // !(defined(ABSL_HAVE_STD_VARIANT) && +#endif // !(defined(ABSL_USES_STD_VARIANT) && // defined(_GLIBCXX_RELEASE) && _GLIBCXX_RELEASE == 8) } } @@ -445,7 +445,7 @@ TEST(VariantExceptionSafetyTest, ValueAssign) { // and operator=(variant&&) invokes Tj's move ctor which doesn't throw. // libstdc++ std::variant has bugs on conversion assignment regarding // exception safety. -#if !(defined(ABSL_HAVE_STD_VARIANT) && defined(__GLIBCXX__)) +#if !(defined(ABSL_USES_STD_VARIANT) && defined(__GLIBCXX__)) { MoveNothrow rhs; EXPECT_TRUE(MakeExceptionSafetyTester() @@ -453,7 +453,7 @@ TEST(VariantExceptionSafetyTest, ValueAssign) { .WithContracts(VariantInvariants, strong_guarantee) .Test([&rhs](ThrowingVariant* lhs) { *lhs = rhs; })); } -#endif // !(defined(ABSL_HAVE_STD_VARIANT) && defined(__GLIBCXX__)) +#endif // !(defined(ABSL_USES_STD_VARIANT) && defined(__GLIBCXX__)) } TEST(VariantExceptionSafetyTest, Emplace) { @@ -527,4 +527,4 @@ TEST(VariantExceptionSafetyTest, Swap) { #endif // !defined(ABSL_INTERNAL_MSVC_2017_DBG_MODE) -#endif // #if !defined(ABSL_HAVE_STD_VARIANT) && defined(ABSL_HAVE_EXCEPTIONS) +#endif // #if !defined(ABSL_USES_STD_VARIANT) && defined(ABSL_HAVE_EXCEPTIONS) diff --git a/absl/types/variant_test.cc b/absl/types/variant_test.cc index b5912745714..2913775a4e5 100644 --- a/absl/types/variant_test.cc +++ b/absl/types/variant_test.cc @@ -20,7 +20,7 @@ #include "absl/types/variant.h" // This test is a no-op when absl::variant is an alias for std::variant. -#if !defined(ABSL_HAVE_STD_VARIANT) +#if !defined(ABSL_USES_STD_VARIANT) #include #include @@ -842,7 +842,7 @@ TEST(VariantTest, TestBackupAssign) { } // libstdc++ doesn't pass this test -#if !(defined(ABSL_HAVE_STD_VARIANT) && defined(__GLIBCXX__)) +#if !(defined(ABSL_USES_STD_VARIANT) && defined(__GLIBCXX__)) EXPECT_EQ(3, counter[0]); EXPECT_EQ(2, counter[1]); EXPECT_EQ(2, counter[2]); @@ -1933,7 +1933,7 @@ TEST(VariantTest, VisitReferenceWrapper) { } // libstdc++ std::variant doesn't support the INVOKE semantics. -#if !(defined(ABSL_HAVE_STD_VARIANT) && defined(__GLIBCXX__)) +#if !(defined(ABSL_USES_STD_VARIANT) && defined(__GLIBCXX__)) TEST(VariantTest, VisitMemberFunction) { absl::variant> p(absl::make_unique()); absl::variant> cp( @@ -1957,7 +1957,7 @@ TEST(VariantTest, VisitDataMember) { EXPECT_EQ(42, absl::visit(&Class::member, cp)); } -#endif // !(defined(ABSL_HAVE_STD_VARIANT) && defined(__GLIBCXX__)) +#endif // !(defined(ABSL_USES_STD_VARIANT) && defined(__GLIBCXX__)) ///////////////////////// // [variant.monostate] // @@ -2035,7 +2035,7 @@ TEST(VariantTest, NonmemberSwap) { std::swap(a, b); EXPECT_THAT(a, VariantWith(v2)); EXPECT_THAT(b, VariantWith(v1)); -#ifndef ABSL_HAVE_STD_VARIANT +#ifndef ABSL_USES_STD_VARIANT EXPECT_FALSE(absl::get(a).special_swap); #endif @@ -2083,7 +2083,7 @@ TEST(VariantTest, Hash) { // MSVC std::hash does not use the index, thus produce the same // result on the same value as different alternative. -#if !(defined(_MSC_VER) && defined(ABSL_HAVE_STD_VARIANT)) +#if !(defined(_MSC_VER) && defined(ABSL_USES_STD_VARIANT)) { // same value as different alternative variant v0(in_place_index<0>, 42); @@ -2091,7 +2091,7 @@ TEST(VariantTest, Hash) { std::hash> hash; EXPECT_NE(hash(v0), hash(v1)); } -#endif // !(defined(_MSC_VER) && defined(ABSL_HAVE_STD_VARIANT)) +#endif // !(defined(_MSC_VER) && defined(ABSL_USES_STD_VARIANT)) { std::hash> hash; @@ -2118,7 +2118,7 @@ TEST(VariantTest, Hash) { //////////////////////////////////////// // Test that a set requiring a basic type conversion works correctly -#if !defined(ABSL_HAVE_STD_VARIANT) +#if !defined(ABSL_USES_STD_VARIANT) TEST(VariantTest, TestConvertingSet) { typedef variant Variant; Variant v(1.0); @@ -2128,7 +2128,7 @@ TEST(VariantTest, TestConvertingSet) { ASSERT_TRUE(nullptr != absl::get_if(&v)); EXPECT_DOUBLE_EQ(2, absl::get(v)); } -#endif // ABSL_HAVE_STD_VARIANT +#endif // ABSL_USES_STD_VARIANT // Test that a vector of variants behaves reasonably. TEST(VariantTest, Container) { @@ -2280,7 +2280,7 @@ struct Convertible2 { }; TEST(VariantTest, TestRvalueConversion) { -#if !defined(ABSL_HAVE_STD_VARIANT) +#if !defined(ABSL_USES_STD_VARIANT) variant var( ConvertVariantTo>( variant(0))); @@ -2313,7 +2313,7 @@ TEST(VariantTest, TestRvalueConversion) { variant2 = ConvertVariantTo>(variant(42)); ASSERT_TRUE(absl::holds_alternative(variant2)); EXPECT_EQ(42, absl::get(variant2)); -#endif // !ABSL_HAVE_STD_VARIANT +#endif // !ABSL_USES_STD_VARIANT variant variant3( ConvertVariantTo>( @@ -2326,7 +2326,7 @@ TEST(VariantTest, TestRvalueConversion) { } TEST(VariantTest, TestLvalueConversion) { -#if !defined(ABSL_HAVE_STD_VARIANT) +#if !defined(ABSL_USES_STD_VARIANT) variant source1 = 0; variant destination( ConvertVariantTo>(source1)); @@ -2428,7 +2428,7 @@ TEST(VariantTest, DoesNotMoveFromLvalues) { } TEST(VariantTest, TestRvalueConversionViaConvertVariantTo) { -#if !defined(ABSL_HAVE_STD_VARIANT) +#if !defined(ABSL_USES_STD_VARIANT) variant var( ConvertVariantTo>( variant(3))); @@ -2467,7 +2467,7 @@ TEST(VariantTest, TestRvalueConversionViaConvertVariantTo) { } TEST(VariantTest, TestLvalueConversionViaConvertVariantTo) { -#if !defined(ABSL_HAVE_STD_VARIANT) +#if !defined(ABSL_USES_STD_VARIANT) variant source1 = 3; variant destination( ConvertVariantTo>(source1)); @@ -2499,7 +2499,7 @@ TEST(VariantTest, TestLvalueConversionViaConvertVariantTo) { variant source6(42); variant2 = ConvertVariantTo>(source6); EXPECT_THAT(absl::get_if(&variant2), Pointee(42)); -#endif // !ABSL_HAVE_STD_VARIANT +#endif // !ABSL_USES_STD_VARIANT variant source7((Convertible1())); variant variant3( @@ -2533,7 +2533,7 @@ TEST(VariantTest, TestMoveConversionViaConvertVariantTo) { // standard and we know that libstdc++ variant doesn't have this feature. // For more details see the paper: // http://open-std.org/JTC1/SC22/WG21/docs/papers/2017/p0602r0.html -#if !(defined(ABSL_HAVE_STD_VARIANT) && defined(__GLIBCXX__)) +#if !(defined(ABSL_USES_STD_VARIANT) && defined(__GLIBCXX__)) #define ABSL_VARIANT_PROPAGATE_COPY_MOVE_TRIVIALITY 1 #endif @@ -2711,4 +2711,4 @@ TEST(VariantTest, MoveCtorBug) { } // namespace } // namespace absl -#endif // #if !defined(ABSL_HAVE_STD_VARIANT) +#endif // #if !defined(ABSL_USES_STD_VARIANT) diff --git a/absl/utility/utility.h b/absl/utility/utility.h index eef8fb41fe8..5a98c2c39c4 100644 --- a/absl/utility/utility.h +++ b/absl/utility/utility.h @@ -158,12 +158,12 @@ using index_sequence_for = make_index_sequence; // Tag types -#ifdef ABSL_HAVE_STD_OPTIONAL +#ifdef ABSL_USES_STD_OPTIONAL using std::in_place_t; using std::in_place; -#else // ABSL_HAVE_STD_OPTIONAL +#else // ABSL_USES_STD_OPTIONAL // in_place_t // @@ -174,9 +174,9 @@ struct in_place_t {}; ABSL_INTERNAL_INLINE_CONSTEXPR(in_place_t, in_place, {}); -#endif // ABSL_HAVE_STD_OPTIONAL +#endif // ABSL_USES_STD_OPTIONAL -#if defined(ABSL_HAVE_STD_ANY) || defined(ABSL_HAVE_STD_VARIANT) +#if defined(ABSL_USES_STD_ANY) || defined(ABSL_USES_STD_VARIANT) using std::in_place_type; using std::in_place_type_t; #else @@ -191,9 +191,9 @@ using in_place_type_t = void (*)(utility_internal::InPlaceTypeTag); template void in_place_type(utility_internal::InPlaceTypeTag) {} -#endif // ABSL_HAVE_STD_ANY || ABSL_HAVE_STD_VARIANT +#endif // ABSL_USES_STD_ANY || ABSL_USES_STD_VARIANT -#ifdef ABSL_HAVE_STD_VARIANT +#ifdef ABSL_USES_STD_VARIANT using std::in_place_index; using std::in_place_index_t; #else @@ -208,7 +208,7 @@ using in_place_index_t = void (*)(utility_internal::InPlaceIndexTag); template void in_place_index(utility_internal::InPlaceIndexTag) {} -#endif // ABSL_HAVE_STD_VARIANT +#endif // ABSL_USES_STD_VARIANT // Constexpr move and forward From 846e5dbedac123d12455adcfe6f53c8b5dcbfeef Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Thu, 31 Oct 2019 11:37:35 -0700 Subject: [PATCH 0013/3164] Export of internal Abseil changes -- 90ecacd2a3db96ee64ef23af37a80fad404e2b32 by Gennadiy Rozental : Fixes MSVC regression by making MSVC version of class Flag into an aggregate type. PiperOrigin-RevId: 277767054 -- 018f3b040df51d91a988fa146fee163721e605e9 by Abseil Team : Change libstdc++ lacking std::unique_ptr check from a gcc version check to based on feature macros. PiperOrigin-RevId: 277736042 -- 475844775ae343e2414318f08549ee3fa6676a8d by CJ Johnson : Pass allocator_type through allocator_traits before extracting the typedefs PiperOrigin-RevId: 277730393 -- d843bc4bc30bf5b11af76db8beda8634b6111a62 by Abseil Team : Convert the Waiter::Init() method to the default constructor and define a destructor for the Waiter class. Use placement new and delete with Waiter objects. PiperOrigin-RevId: 277728823 -- 1ba6edf421dd2dfe13c55970a03c99592cb6677d by Derek Mauro : Use lowercase spelling for include of dbghelp.h When cross-compiling under MinGW this is important PiperOrigin-RevId: 277629783 -- cfc662a6fa357a84ddda8037156c7f26cee40c36 by Abseil Team : Don't use atomic ops on waiter and wakeup counts in WIN32 waiter mode. Port the new CONDVAR waiter mode code in CL 277366017 to the WIN32 waiter mode. PiperOrigin-RevId: 277603611 -- 833106542e61fa0832900adf3c1b2afc6890b94b by Abseil Team : Add the PerThreadSem::Destroy() method. For ABSL_WAITER_MODE_CONDVAR or ABSL_WAITER_MODE_SEM, PerThreadSem::Destroy() is used to destroy the pthread mutex and condition variable or the POSIX semaphore. PiperOrigin-RevId: 277586675 -- 7814da4a59106cf1e0e4db1a31b9592ebbd2094b by Samuel Benzaquen : Enable the assertion in the iterator's operator* and operator-> PiperOrigin-RevId: 277563401 GitOrigin-RevId: 90ecacd2a3db96ee64ef23af37a80fad404e2b32 Change-Id: Ib19be3680da74f0b94055c9039115ec6bcaea7b0 --- absl/container/inlined_vector.h | 13 +- absl/container/inlined_vector_test.cc | 24 +++ absl/container/internal/inlined_vector.h | 138 +++++++++--------- absl/container/internal/raw_hash_set.h | 2 +- absl/debugging/symbolize_win32.inc | 4 +- absl/flags/flag.h | 70 ++++++--- absl/memory/memory.h | 9 +- .../internal/create_thread_identity.cc | 4 +- .../internal/create_thread_identity.h | 4 + .../internal/per_thread_sem.cc | 6 +- .../synchronization/internal/per_thread_sem.h | 5 + absl/synchronization/internal/waiter.cc | 77 ++++++---- absl/synchronization/internal/waiter.h | 27 ++-- 13 files changed, 239 insertions(+), 144 deletions(-) diff --git a/absl/container/inlined_vector.h b/absl/container/inlined_vector.h index 5138348a15e..d5c67db5984 100644 --- a/absl/container/inlined_vector.h +++ b/absl/container/inlined_vector.h @@ -69,9 +69,10 @@ class InlinedVector { static_assert(N > 0, "`absl::InlinedVector` requires an inlined capacity."); using Storage = inlined_vector_internal::Storage; - using rvalue_reference = typename Storage::rvalue_reference; - using MoveIterator = typename Storage::MoveIterator; + using AllocatorTraits = typename Storage::AllocatorTraits; + using RValueReference = typename Storage::RValueReference; + using MoveIterator = typename Storage::MoveIterator; using IsMemcpyOk = typename Storage::IsMemcpyOk; template @@ -92,10 +93,10 @@ class InlinedVector { using value_type = typename Storage::value_type; using pointer = typename Storage::pointer; using const_pointer = typename Storage::const_pointer; - using reference = typename Storage::reference; - using const_reference = typename Storage::const_reference; using size_type = typename Storage::size_type; using difference_type = typename Storage::difference_type; + using reference = typename Storage::reference; + using const_reference = typename Storage::const_reference; using iterator = typename Storage::iterator; using const_iterator = typename Storage::const_iterator; using reverse_iterator = typename Storage::reverse_iterator; @@ -563,7 +564,7 @@ class InlinedVector { // Overload of `InlinedVector::insert(...)` that inserts `v` at `pos` using // move semantics, returning an `iterator` to the newly inserted element. - iterator insert(const_iterator pos, rvalue_reference v) { + iterator insert(const_iterator pos, RValueReference v) { return emplace(pos, std::move(v)); } @@ -660,7 +661,7 @@ class InlinedVector { // Overload of `InlinedVector::push_back(...)` for inserting `v` at `end()` // using move semantics. - void push_back(rvalue_reference v) { + void push_back(RValueReference v) { static_cast(emplace_back(std::move(v))); } diff --git a/absl/container/inlined_vector_test.cc b/absl/container/inlined_vector_test.cc index 080ea95665d..2c9b0d0e039 100644 --- a/absl/container/inlined_vector_test.cc +++ b/absl/container/inlined_vector_test.cc @@ -1754,6 +1754,30 @@ TEST(AllocatorSupportTest, SizeAllocConstructor) { } } +TEST(InlinedVectorTest, MinimumAllocatorCompilesUsingTraits) { + using T = int; + using A = std::allocator; + using ATraits = absl::allocator_traits; + + struct MinimumAllocator { + using value_type = T; + + value_type* allocate(size_t n) { + A a; + return ATraits::allocate(a, n); + } + + void deallocate(value_type* p, size_t n) { + A a; + ATraits::deallocate(a, p, n); + } + }; + + absl::InlinedVector vec; + vec.emplace_back(); + vec.resize(0); +} + TEST(InlinedVectorTest, AbslHashValueWorks) { using V = absl::InlinedVector; std::vector cases; diff --git a/absl/container/internal/inlined_vector.h b/absl/container/internal/inlined_vector.h index f678c88dafd..84aa785ad63 100644 --- a/absl/container/internal/inlined_vector.h +++ b/absl/container/internal/inlined_vector.h @@ -37,16 +37,17 @@ using IsAtLeastForwardIterator = std::is_convertible< typename std::iterator_traits::iterator_category, std::forward_iterator_tag>; -template -using IsMemcpyOk = absl::conjunction< - std::is_same, - AllocatorType>, - absl::is_trivially_copy_constructible, - absl::is_trivially_copy_assignable, - absl::is_trivially_destructible>; - -template -void DestroyElements(AllocatorType* alloc_ptr, ValueType* destroy_first, +template ::value_type> +using IsMemcpyOk = + absl::conjunction>, + absl::is_trivially_copy_constructible, + absl::is_trivially_copy_assignable, + absl::is_trivially_destructible>; + +template +void DestroyElements(AllocatorType* alloc_ptr, Pointer destroy_first, SizeType destroy_size) { using AllocatorTraits = absl::allocator_traits; @@ -57,20 +58,25 @@ void DestroyElements(AllocatorType* alloc_ptr, ValueType* destroy_first, } #if !defined(NDEBUG) - // Overwrite unused memory with `0xab` so we can catch uninitialized usage. - // - // Cast to `void*` to tell the compiler that we don't care that we might be - // scribbling on a vtable pointer. - auto* memory_ptr = static_cast(destroy_first); - auto memory_size = sizeof(ValueType) * destroy_size; - std::memset(memory_ptr, 0xab, memory_size); + { + using ValueType = typename AllocatorTraits::value_type; + + // Overwrite unused memory with `0xab` so we can catch uninitialized + // usage. + // + // Cast to `void*` to tell the compiler that we don't care that we might + // be scribbling on a vtable pointer. + void* memory_ptr = destroy_first; + auto memory_size = destroy_size * sizeof(ValueType); + std::memset(memory_ptr, 0xab, memory_size); + } #endif // !defined(NDEBUG) } } -template -void ConstructElements(AllocatorType* alloc_ptr, ValueType* construct_first, +void ConstructElements(AllocatorType* alloc_ptr, Pointer construct_first, ValueAdapter* values_ptr, SizeType construct_size) { for (SizeType i = 0; i < construct_size; ++i) { ABSL_INTERNAL_TRY { @@ -83,8 +89,8 @@ void ConstructElements(AllocatorType* alloc_ptr, ValueType* construct_first, } } -template -void AssignElements(ValueType* assign_first, ValueAdapter* values_ptr, +template +void AssignElements(Pointer assign_first, ValueAdapter* values_ptr, SizeType assign_size) { for (SizeType i = 0; i < assign_size; ++i) { values_ptr->AssignNext(assign_first + i); @@ -93,28 +99,29 @@ void AssignElements(ValueType* assign_first, ValueAdapter* values_ptr, template struct StorageView { - using pointer = typename AllocatorType::pointer; - using size_type = typename AllocatorType::size_type; + using AllocatorTraits = absl::allocator_traits; + using Pointer = typename AllocatorTraits::pointer; + using SizeType = typename AllocatorTraits::size_type; - pointer data; - size_type size; - size_type capacity; + Pointer data; + SizeType size; + SizeType capacity; }; template class IteratorValueAdapter { - using pointer = typename AllocatorType::pointer; using AllocatorTraits = absl::allocator_traits; + using Pointer = typename AllocatorTraits::pointer; public: explicit IteratorValueAdapter(const Iterator& it) : it_(it) {} - void ConstructNext(AllocatorType* alloc_ptr, pointer construct_at) { + void ConstructNext(AllocatorType* alloc_ptr, Pointer construct_at) { AllocatorTraits::construct(*alloc_ptr, construct_at, *it_); ++it_; } - void AssignNext(pointer assign_at) { + void AssignNext(Pointer assign_at) { *assign_at = *it_; ++it_; } @@ -125,46 +132,45 @@ class IteratorValueAdapter { template class CopyValueAdapter { - using pointer = typename AllocatorType::pointer; - using const_pointer = typename AllocatorType::const_pointer; - using const_reference = typename AllocatorType::const_reference; using AllocatorTraits = absl::allocator_traits; + using ValueType = typename AllocatorTraits::value_type; + using Pointer = typename AllocatorTraits::pointer; + using ConstPointer = typename AllocatorTraits::const_pointer; public: - explicit CopyValueAdapter(const_reference v) : ptr_(std::addressof(v)) {} + explicit CopyValueAdapter(const ValueType& v) : ptr_(std::addressof(v)) {} - void ConstructNext(AllocatorType* alloc_ptr, pointer construct_at) { + void ConstructNext(AllocatorType* alloc_ptr, Pointer construct_at) { AllocatorTraits::construct(*alloc_ptr, construct_at, *ptr_); } - void AssignNext(pointer assign_at) { *assign_at = *ptr_; } + void AssignNext(Pointer assign_at) { *assign_at = *ptr_; } private: - const_pointer ptr_; + ConstPointer ptr_; }; template class DefaultValueAdapter { - using pointer = typename AllocatorType::pointer; - using value_type = typename AllocatorType::value_type; using AllocatorTraits = absl::allocator_traits; + using ValueType = typename AllocatorTraits::value_type; + using Pointer = typename AllocatorTraits::pointer; public: explicit DefaultValueAdapter() {} - void ConstructNext(AllocatorType* alloc_ptr, pointer construct_at) { + void ConstructNext(AllocatorType* alloc_ptr, Pointer construct_at) { AllocatorTraits::construct(*alloc_ptr, construct_at); } - void AssignNext(pointer assign_at) { *assign_at = value_type(); } + void AssignNext(Pointer assign_at) { *assign_at = ValueType(); } }; template 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; + using Pointer = typename AllocatorTraits::pointer; + using SizeType = typename AllocatorTraits::size_type; public: explicit AllocationTransaction(AllocatorType* alloc_ptr) @@ -180,11 +186,11 @@ class AllocationTransaction { void operator=(const AllocationTransaction&) = delete; AllocatorType& GetAllocator() { return alloc_data_.template get<0>(); } - pointer& GetData() { return alloc_data_.template get<1>(); } - size_type& GetCapacity() { return capacity_; } + Pointer& GetData() { return alloc_data_.template get<1>(); } + SizeType& GetCapacity() { return capacity_; } bool DidAllocate() { return GetData() != nullptr; } - pointer Allocate(size_type capacity) { + Pointer Allocate(SizeType capacity) { GetData() = AllocatorTraits::allocate(GetAllocator(), capacity); GetCapacity() = capacity; return GetData(); @@ -196,14 +202,15 @@ class AllocationTransaction { } private: - container_internal::CompressedTuple alloc_data_; - size_type capacity_ = 0; + container_internal::CompressedTuple alloc_data_; + SizeType capacity_ = 0; }; template class ConstructionTransaction { - using pointer = typename AllocatorType::pointer; - using size_type = typename AllocatorType::size_type; + using AllocatorTraits = absl::allocator_traits; + using Pointer = typename AllocatorTraits::pointer; + using SizeType = typename AllocatorTraits::size_type; public: explicit ConstructionTransaction(AllocatorType* alloc_ptr) @@ -220,12 +227,12 @@ class ConstructionTransaction { void operator=(const ConstructionTransaction&) = delete; AllocatorType& GetAllocator() { return alloc_data_.template get<0>(); } - pointer& GetData() { return alloc_data_.template get<1>(); } - size_type& GetSize() { return size_; } + Pointer& GetData() { return alloc_data_.template get<1>(); } + SizeType& GetSize() { return size_; } bool DidConstruct() { return GetData() != nullptr; } template - void Construct(pointer data, ValueAdapter* values_ptr, size_type size) { + void Construct(Pointer data, ValueAdapter* values_ptr, SizeType size) { inlined_vector_internal::ConstructElements(std::addressof(GetAllocator()), data, values_ptr, size); GetData() = data; @@ -237,28 +244,29 @@ class ConstructionTransaction { } private: - container_internal::CompressedTuple alloc_data_; - size_type size_ = 0; + container_internal::CompressedTuple alloc_data_; + SizeType size_ = 0; }; template class Storage { public: - using allocator_type = A; - using value_type = typename allocator_type::value_type; - using pointer = typename allocator_type::pointer; - using const_pointer = typename allocator_type::const_pointer; - using reference = typename allocator_type::reference; - using const_reference = typename allocator_type::const_reference; - using rvalue_reference = typename allocator_type::value_type&&; - using size_type = typename allocator_type::size_type; - using difference_type = typename allocator_type::difference_type; + using AllocatorTraits = absl::allocator_traits; + using allocator_type = typename AllocatorTraits::allocator_type; + using value_type = typename AllocatorTraits::value_type; + using pointer = typename AllocatorTraits::pointer; + using const_pointer = typename AllocatorTraits::const_pointer; + using size_type = typename AllocatorTraits::size_type; + using difference_type = typename AllocatorTraits::difference_type; + + using reference = value_type&; + using const_reference = const value_type&; + using RValueReference = value_type&&; using iterator = pointer; using const_iterator = const_pointer; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; using MoveIterator = std::move_iterator; - using AllocatorTraits = absl::allocator_traits; using IsMemcpyOk = inlined_vector_internal::IsMemcpyOk; using StorageView = inlined_vector_internal::StorageView; diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index 9079a73e52a..bf0c03c44f2 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h @@ -615,7 +615,7 @@ class raw_hash_set { // PRECONDITION: not an end() iterator. reference operator*() const { - /* To be enabled: assert_is_full(); */ + assert_is_full(); return PolicyTraits::element(slot_); } diff --git a/absl/debugging/symbolize_win32.inc b/absl/debugging/symbolize_win32.inc index 0fb4b803769..b0fb3b9a3e1 100644 --- a/absl/debugging/symbolize_win32.inc +++ b/absl/debugging/symbolize_win32.inc @@ -17,10 +17,10 @@ #include -// MSVC header DbgHelp.h has a warning for an ignored typedef. +// MSVC header dbghelp.h has a warning for an ignored typedef. #pragma warning(push) #pragma warning(disable:4091) -#include +#include #pragma warning(pop) #pragma comment(lib, "dbghelp.lib") diff --git a/absl/flags/flag.h b/absl/flags/flag.h index c0060b449a8..356ddb61607 100644 --- a/absl/flags/flag.h +++ b/absl/flags/flag.h @@ -67,17 +67,20 @@ namespace absl { template using Flag = flags_internal::Flag; #else -// MSVC debug builds do not implement constexpr correctly for classes with -// virtual methods. To work around this we adding level of indirection, so that -// the class `absl::Flag` contains an `internal::Flag*` (instead of being an -// alias to that class) and dynamically allocates an instance when necessary. -// We also forward all calls to internal::Flag methods via trampoline methods. -// In this setup the `absl::Flag` class does not have virtual methods and thus -// MSVC is able to initialize it at link time. To deal with multiple threads -// accessing the flag for the first time concurrently we use an atomic boolean -// indicating if flag object is constructed. We also employ the double-checked -// locking pattern where the second level of protection is a global Mutex, so -// if two threads attempt to construct the flag concurrently only one wins. +// MSVC debug builds do not implement initialization with constexpr constructors +// correctly. To work around this we add a level of indirection, so that the +// class `absl::Flag` contains an `internal::Flag*` (instead of being an alias +// to that class) and dynamically allocates an instance when necessary. We also +// forward all calls to internal::Flag methods via trampoline methods. In this +// setup the `absl::Flag` class does not have constructor and virtual methods, +// all the data members are public and thus MSVC is able to initialize it at +// link time. To deal with multiple threads accessing the flag for the first +// time concurrently we use an atomic boolean indicating if flag object is +// initialized. We also employ the double-checked locking pattern where the +// second level of protection is a global Mutex, so if two threads attempt to +// construct the flag concurrently only one wins. +// This solution is based on a recomendation here: +// https://developercommunity.visualstudio.com/content/problem/336946/class-with-constexpr-constructor-not-using-static.html?childToView=648454#comment-648454 namespace flags_internal { absl::Mutex* GetGlobalConstructionGuard(); @@ -86,16 +89,23 @@ absl::Mutex* GetGlobalConstructionGuard(); template class Flag { public: + // No constructor and destructor to ensure this is an aggregate type. + // Visual Studio 2015 still requires the constructor for class to be + // constexpr initializable. +#if _MSC_VER <= 1900 constexpr Flag(const char* name, const flags_internal::HelpGenFunc help_gen, const char* filename, const flags_internal::FlagMarshallingOpFn marshalling_op, - const flags_internal::InitialValGenFunc initial_value_gen) + const flags_internal::InitialValGenFunc initial_value_gen, + bool, void*) : name_(name), help_gen_(help_gen), filename_(filename), marshalling_op_(marshalling_op), initial_value_gen_(initial_value_gen), - inited_(false) {} + inited_(false), + impl_(nullptr) {} +#endif flags_internal::Flag* GetImpl() const { if (!inited_.load(std::memory_order_acquire)) { @@ -113,7 +123,8 @@ class Flag { return impl_; } - // absl::Flag API + // Public methods of `absl::Flag` are NOT part of the Abseil Flags API. + // See https://abseil.io/docs/cpp/guides/flags bool IsRetired() const { return GetImpl()->IsRetired(); } bool IsAbseilFlag() const { return GetImpl()->IsAbseilFlag(); } absl::string_view Name() const { return GetImpl()->Name(); } @@ -126,9 +137,9 @@ class Flag { std::string Filename() const { return GetImpl()->Filename(); } std::string DefaultValue() const { return GetImpl()->DefaultValue(); } std::string CurrentValue() const { return GetImpl()->CurrentValue(); } - template + template inline bool IsOfType() const { - return GetImpl()->template IsOfType(); + return GetImpl()->template IsOfType(); } T Get() const { return GetImpl()->Get(); } bool AtomicGet(T* v) const { return GetImpl()->AtomicGet(v); } @@ -138,7 +149,8 @@ class Flag { } void InvokeCallback() { GetImpl()->InvokeCallback(); } - private: + // The data members are logically private, but they need to be public for + // this to be an aggregate type. const char* name_; const flags_internal::HelpGenFunc help_gen_; const char* filename_; @@ -146,7 +158,7 @@ class Flag { const flags_internal::InitialValGenFunc initial_value_gen_; mutable std::atomic inited_; - mutable flags_internal::Flag* impl_ = nullptr; + mutable flags_internal::Flag* impl_; }; #endif @@ -310,17 +322,35 @@ void SetFlag(absl::Flag* flag, const V& v) { // Note: Name of registrar object is not arbitrary. It is used to "grab" // global name for FLAGS_no symbol, thus preventing the possibility // of defining two flags with names foo and nofoo. +#if !defined(_MSC_VER) || defined(__clang__) #define ABSL_FLAG_IMPL(Type, name, default_value, help) \ namespace absl /* block flags in namespaces */ {} \ ABSL_FLAG_IMPL_DECLARE_DEF_VAL_WRAPPER(name, Type, default_value) \ ABSL_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, help) \ - ABSL_CONST_INIT absl::Flag FLAGS_##name( \ + ABSL_CONST_INIT absl::Flag FLAGS_##name{ \ ABSL_FLAG_IMPL_FLAGNAME(#name), &AbslFlagsWrapHelp##name, \ ABSL_FLAG_IMPL_FILENAME(), \ &absl::flags_internal::FlagMarshallingOps, \ - &AbslFlagsInitFlag##name); \ + &AbslFlagsInitFlag##name}; \ + extern bool FLAGS_no##name; \ + bool FLAGS_no##name = ABSL_FLAG_IMPL_REGISTRAR(Type, FLAGS_##name) +#else +// MSVC version uses aggregate initialization. +#define ABSL_FLAG_IMPL(Type, name, default_value, help) \ + namespace absl /* block flags in namespaces */ {} \ + ABSL_FLAG_IMPL_DECLARE_DEF_VAL_WRAPPER(name, Type, default_value) \ + ABSL_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, help) \ + ABSL_CONST_INIT absl::Flag FLAGS_##name{ \ + ABSL_FLAG_IMPL_FLAGNAME(#name), \ + &AbslFlagsWrapHelp##name, \ + ABSL_FLAG_IMPL_FILENAME(), \ + &absl::flags_internal::FlagMarshallingOps, \ + &AbslFlagsInitFlag##name, \ + false, \ + nullptr}; \ extern bool FLAGS_no##name; \ bool FLAGS_no##name = ABSL_FLAG_IMPL_REGISTRAR(Type, FLAGS_##name) +#endif // ABSL_RETIRED_FLAG // diff --git a/absl/memory/memory.h b/absl/memory/memory.h index 5a4a1a1dfe2..243a5dda6ee 100644 --- a/absl/memory/memory.h +++ b/absl/memory/memory.h @@ -92,11 +92,12 @@ struct MakeUniqueResult { } // namespace memory_internal -// gcc 4.8 has __cplusplus at 201301 but doesn't define make_unique. Other -// supported compilers either just define __cplusplus as 201103 but have -// make_unique (msvc), or have make_unique whenever __cplusplus > 201103 (clang) +// gcc 4.8 has __cplusplus at 201301 but the libstdc++ shipped with it doesn't +// define make_unique. Other supported compilers either just define __cplusplus +// as 201103 but have make_unique (msvc), or have make_unique whenever +// __cplusplus > 201103 (clang). #if (__cplusplus > 201103L || defined(_MSC_VER)) && \ - !(defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 8) + !(defined(__GLIBCXX__) && !defined(__cpp_lib_make_unique)) using std::make_unique; #else // ----------------------------------------------------------------------------- diff --git a/absl/synchronization/internal/create_thread_identity.cc b/absl/synchronization/internal/create_thread_identity.cc index ce3676a8848..eef661f23d1 100644 --- a/absl/synchronization/internal/create_thread_identity.cc +++ b/absl/synchronization/internal/create_thread_identity.cc @@ -37,7 +37,7 @@ static base_internal::ThreadIdentity* thread_identity_freelist; // A per-thread destructor for reclaiming associated ThreadIdentity objects. // Since we must preserve their storage we cache them for re-use. -static void ReclaimThreadIdentity(void* v) { +void ReclaimThreadIdentity(void* v) { base_internal::ThreadIdentity* identity = static_cast(v); @@ -47,6 +47,8 @@ static void ReclaimThreadIdentity(void* v) { base_internal::LowLevelAlloc::Free(identity->per_thread_synch.all_locks); } + PerThreadSem::Destroy(identity); + // We must explicitly clear the current thread's identity: // (a) Subsequent (unrelated) per-thread destructors may require an identity. // We must guarantee a new identity is used in this case (this instructor diff --git a/absl/synchronization/internal/create_thread_identity.h b/absl/synchronization/internal/create_thread_identity.h index ebb16c56a7c..97237a6ede9 100644 --- a/absl/synchronization/internal/create_thread_identity.h +++ b/absl/synchronization/internal/create_thread_identity.h @@ -35,6 +35,10 @@ namespace synchronization_internal { // For private use only. base_internal::ThreadIdentity* CreateThreadIdentity(); +// A per-thread destructor for reclaiming associated ThreadIdentity objects. +// For private use only. +void ReclaimThreadIdentity(void* v); + // Returns the ThreadIdentity object representing the calling thread; guaranteed // to be unique for its lifetime. The returned object will remain valid for the // program's lifetime; although it may be re-assigned to a subsequent thread. diff --git a/absl/synchronization/internal/per_thread_sem.cc b/absl/synchronization/internal/per_thread_sem.cc index b7014fb2b02..2a78b20fc88 100644 --- a/absl/synchronization/internal/per_thread_sem.cc +++ b/absl/synchronization/internal/per_thread_sem.cc @@ -40,12 +40,16 @@ std::atomic *PerThreadSem::GetThreadBlockedCounter() { } void PerThreadSem::Init(base_internal::ThreadIdentity *identity) { - Waiter::GetWaiter(identity)->Init(); + new (Waiter::GetWaiter(identity)) Waiter(); identity->ticker.store(0, std::memory_order_relaxed); identity->wait_start.store(0, std::memory_order_relaxed); identity->is_idle.store(false, std::memory_order_relaxed); } +void PerThreadSem::Destroy(base_internal::ThreadIdentity *identity) { + Waiter::GetWaiter(identity)->~Waiter(); +} + void PerThreadSem::Tick(base_internal::ThreadIdentity *identity) { const int ticker = identity->ticker.fetch_add(1, std::memory_order_relaxed) + 1; diff --git a/absl/synchronization/internal/per_thread_sem.h b/absl/synchronization/internal/per_thread_sem.h index e7da070ba72..113cdfb7dc1 100644 --- a/absl/synchronization/internal/per_thread_sem.h +++ b/absl/synchronization/internal/per_thread_sem.h @@ -65,6 +65,10 @@ class PerThreadSem { // REQUIRES: May only be called by ThreadIdentity. static void Init(base_internal::ThreadIdentity* identity); + // Destroy the PerThreadSem associated with "identity". + // REQUIRES: May only be called by ThreadIdentity. + static void Destroy(base_internal::ThreadIdentity* identity); + // Increments "identity"'s count. static inline void Post(base_internal::ThreadIdentity* identity); @@ -77,6 +81,7 @@ class PerThreadSem { friend class PerThreadSemTest; friend class absl::Mutex; friend absl::base_internal::ThreadIdentity* CreateThreadIdentity(); + friend void ReclaimThreadIdentity(void* v); }; } // namespace synchronization_internal diff --git a/absl/synchronization/internal/waiter.cc b/absl/synchronization/internal/waiter.cc index 2f41aa5f2b4..8e713770532 100644 --- a/absl/synchronization/internal/waiter.cc +++ b/absl/synchronization/internal/waiter.cc @@ -122,10 +122,12 @@ class Futex { } }; -void Waiter::Init() { +Waiter::Waiter() { futex_.store(0, std::memory_order_relaxed); } +Waiter::~Waiter() = default; + bool Waiter::Wait(KernelTimeout t) { // Loop until we can atomically decrement futex from a positive // value, waiting on a futex while we believe it is zero. @@ -199,7 +201,7 @@ class PthreadMutexHolder { pthread_mutex_t *mu_; }; -void Waiter::Init() { +Waiter::Waiter() { const int err = pthread_mutex_init(&mu_, 0); if (err != 0) { ABSL_RAW_LOG(FATAL, "pthread_mutex_init failed: %d", err); @@ -214,6 +216,18 @@ void Waiter::Init() { wakeup_count_ = 0; } +Waiter::~Waiter() { + const int err = pthread_mutex_destroy(&mu_); + if (err != 0) { + ABSL_RAW_LOG(FATAL, "pthread_mutex_destroy failed: %d", err); + } + + const int err2 = pthread_cond_destroy(&cv_); + if (err2 != 0) { + ABSL_RAW_LOG(FATAL, "pthread_cond_destroy failed: %d", err2); + } +} + bool Waiter::Wait(KernelTimeout t) { struct timespec abs_timeout; if (t.has_timeout()) { @@ -274,13 +288,19 @@ void Waiter::InternalCondVarPoke() { #elif ABSL_WAITER_MODE == ABSL_WAITER_MODE_SEM -void Waiter::Init() { +Waiter::Waiter() { if (sem_init(&sem_, 0, 0) != 0) { ABSL_RAW_LOG(FATAL, "sem_init failed with errno %d\n", errno); } wakeups_.store(0, std::memory_order_relaxed); } +Waiter::~Waiter() { + if (sem_destroy(&sem_) != 0) { + ABSL_RAW_LOG(FATAL, "sem_destroy failed with errno %d\n", errno); + } +} + bool Waiter::Wait(KernelTimeout t) { struct timespec abs_timeout; if (t.has_timeout()) { @@ -388,39 +408,32 @@ class LockHolder { SRWLOCK* mu_; }; -void Waiter::Init() { +Waiter::Waiter() { auto *mu = ::new (static_cast(&mu_storage_)) SRWLOCK; auto *cv = ::new (static_cast(&cv_storage_)) CONDITION_VARIABLE; InitializeSRWLock(mu); InitializeConditionVariable(cv); - waiter_count_.store(0, std::memory_order_relaxed); - wakeup_count_.store(0, std::memory_order_relaxed); + waiter_count_ = 0; + wakeup_count_ = 0; } +// SRW locks and condition variables do not need to be explicitly destroyed. +// https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-initializesrwlock +// https://stackoverflow.com/questions/28975958/why-does-windows-have-no-deleteconditionvariable-function-to-go-together-with +Waiter::~Waiter() = default; + bool Waiter::Wait(KernelTimeout t) { SRWLOCK *mu = WinHelper::GetLock(this); CONDITION_VARIABLE *cv = WinHelper::GetCond(this); LockHolder h(mu); - waiter_count_.fetch_add(1, std::memory_order_relaxed); + ++waiter_count_; // Loop until we find a wakeup to consume or timeout. // Note that, since the thread ticker is just reset, we don't need to check // whether the thread is idle on the very first pass of the loop. bool first_pass = true; - while (true) { - int x = wakeup_count_.load(std::memory_order_relaxed); - if (x != 0) { - if (!wakeup_count_.compare_exchange_weak(x, x - 1, - std::memory_order_acquire, - std::memory_order_relaxed)) { - continue; // Raced with someone, retry. - } - // Successfully consumed a wakeup, we're done. - waiter_count_.fetch_sub(1, std::memory_order_relaxed); - return true; - } - + while (wakeup_count_ == 0) { if (!first_pass) MaybeBecomeIdle(); // No wakeups available, time to wait. if (!SleepConditionVariableSRW(cv, mu, t.InMillisecondsFromNow(), 0)) { @@ -429,7 +442,7 @@ bool Waiter::Wait(KernelTimeout t) { // initialization guarantees this is not a narrowing conversion. const unsigned long err{GetLastError()}; // NOLINT(runtime/int) if (err == ERROR_TIMEOUT) { - waiter_count_.fetch_sub(1, std::memory_order_relaxed); + --waiter_count_; return false; } else { ABSL_RAW_LOG(FATAL, "SleepConditionVariableSRW failed: %lu", err); @@ -437,23 +450,27 @@ bool Waiter::Wait(KernelTimeout t) { } first_pass = false; } + // Consume a wakeup and we're done. + --wakeup_count_; + --waiter_count_; + return true; } void Waiter::Post() { - wakeup_count_.fetch_add(1, std::memory_order_release); - Poke(); + LockHolder h(WinHelper::GetLock(this)); + ++wakeup_count_; + InternalCondVarPoke(); } void Waiter::Poke() { - if (waiter_count_.load(std::memory_order_relaxed) == 0) { - return; - } - // Potentially a waiter. Take the lock and check again. LockHolder h(WinHelper::GetLock(this)); - if (waiter_count_.load(std::memory_order_relaxed) == 0) { - return; + InternalCondVarPoke(); +} + +void Waiter::InternalCondVarPoke() { + if (waiter_count_ != 0) { + WakeConditionVariable(WinHelper::GetCond(this)); } - WakeConditionVariable(WinHelper::GetCond(this)); } #else diff --git a/absl/synchronization/internal/waiter.h b/absl/synchronization/internal/waiter.h index c23897007e7..cac81d5fd22 100644 --- a/absl/synchronization/internal/waiter.h +++ b/absl/synchronization/internal/waiter.h @@ -58,14 +58,15 @@ namespace synchronization_internal { // Waiter is an OS-specific semaphore. class Waiter { public: - // No constructor, instances use the reserved space in ThreadIdentity. - // All initialization logic belongs in `Init()`. - Waiter() = delete; + // Prepare any data to track waits. + Waiter(); + + // Not copyable or movable Waiter(const Waiter&) = delete; Waiter& operator=(const Waiter&) = delete; - // Prepare any data to track waits. - void Init(); + // Destroy any data to track waits. + ~Waiter(); // Blocks the calling thread until a matching call to `Post()` or // `t` has passed. Returns `true` if woken (`Post()` called), @@ -122,13 +123,8 @@ class Waiter { std::atomic wakeups_; #elif ABSL_WAITER_MODE == ABSL_WAITER_MODE_WIN32 - // The Windows API has lots of choices for synchronization - // primivitives. We are using SRWLOCK and CONDITION_VARIABLE - // because they don't require a destructor to release system - // resources. - // - // However, we can't include Windows.h in our headers, so we use aligned - // storage buffers to define the storage. + // We can't include Windows.h in our headers, so we use aligned storage + // buffers to define the storage of SRWLOCK and CONDITION_VARIABLE. using SRWLockStorage = typename std::aligned_storage::type; using ConditionVariableStorage = @@ -138,10 +134,13 @@ class Waiter { // condition variable storage once the types are complete. class WinHelper; + // REQUIRES: WinHelper::GetLock(this) must be held. + void InternalCondVarPoke(); + SRWLockStorage mu_storage_; ConditionVariableStorage cv_storage_; - std::atomic waiter_count_; - std::atomic wakeup_count_; + int waiter_count_; + int wakeup_count_; #else #error Unknown ABSL_WAITER_MODE From 20de2db748ca0471cfb61cb53e813dd12938c12b Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Mon, 4 Nov 2019 07:46:04 -0800 Subject: [PATCH 0014/3164] Export of internal Abseil changes -- 6ac972ce1a042a8925c20cb8b76897b018990de4 by Abseil Team : Improve failure reports in signal handler test. PiperOrigin-RevId: 278377121 GitOrigin-RevId: 6ac972ce1a042a8925c20cb8b76897b018990de4 Change-Id: I74c8cd5c633f2dc77d533556184071a9af6e2768 --- absl/debugging/failure_signal_handler_test.cc | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/absl/debugging/failure_signal_handler_test.cc b/absl/debugging/failure_signal_handler_test.cc index bb2cc48eb4a..863fb5149e7 100644 --- a/absl/debugging/failure_signal_handler_test.cc +++ b/absl/debugging/failure_signal_handler_test.cc @@ -23,6 +23,7 @@ #include #include "gtest/gtest.h" +#include "gmock/gmock.h" #include "absl/base/internal/raw_logging.h" #include "absl/debugging/stacktrace.h" #include "absl/debugging/symbolize.h" @@ -31,6 +32,8 @@ namespace { +using testing::StartsWith; + #if GTEST_HAS_DEATH_TEST // For the parameterized death tests. GetParam() returns the signal number. @@ -113,15 +116,15 @@ TEST_P(FailureSignalHandlerDeathTest, AbslFatalSignalsWithWriterFn) { ASSERT_TRUE(error_output.is_open()) << file; std::string error_line; std::getline(error_output, error_line); - EXPECT_TRUE(absl::StartsWith( + EXPECT_THAT( error_line, - absl::StrCat("*** ", - absl::debugging_internal::FailureSignalToString(signo), - " received at "))); + StartsWith(absl::StrCat( + "*** ", absl::debugging_internal::FailureSignalToString(signo), + " received at "))); if (absl::debugging_internal::StackTraceWorksForTest()) { std::getline(error_output, error_line); - EXPECT_TRUE(absl::StartsWith(error_line, "PC: ")); + EXPECT_THAT(error_line, StartsWith("PC: ")); } } From e96ae2203b80d5ae2e0b7abe0c77b722b224b16d Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Tue, 5 Nov 2019 09:49:15 -0800 Subject: [PATCH 0015/3164] Export of internal Abseil changes -- 074a799119ac881b8b8ce59ef7a3166d1aa025ac by Tom Manshreck : nit: Add return info for StrCat PiperOrigin-RevId: 278647298 -- d58a2a39ab6f50266cc695506ba2e86bdb45d795 by Mark Barolak : Stop suppressing no-nested-anon-types warnings because there aren't actually any warnings to suppress. PiperOrigin-RevId: 278440548 -- 445051bd280b9a6f608a8c80b3d7cafcc1377a03 by Abseil Team : ResetThreadIdentity does not need to clear identity->waiter_state. ResetThreadIdentity is only called by NewThreadIdentity. NewThreadIdentity is only called by CreateThreadIdentity. CreateThreadIdentity calls PerThreadSem::Init, which initializes identity->waiter_state, immediately after calling NewThreadIdentity. Therefore ResetThreadIdentity does not need to clear identity->waiter_state. PiperOrigin-RevId: 278429844 -- c2079b664d92be40d5e365abcca4e9b3505a75a6 by Abseil Team : Delete the f->header.magic check in LowLevelAlloc::Free(). The f->header.magic check in LowLevelAlloc::Free() is redundant, because AddToFreeList() will immediately perform the same check. Also fix a typo in the comment that documents the lock requirements for Next(). The comment should say "L >= arena->mu", which is equivalent to EXCLUSIVE_LOCKS_REQUIRED(arena->mu). NOTE: LowLevelAlloc::Free() performs the f->header.magic check without holding the arena lock. This may have caused the TSAN data race warning reported in bug 143697235. PiperOrigin-RevId: 278414140 -- 5534f35ce677165700117d868f51607ed1f0d73b by Greg Falcon : Add an internal (unsupported) PiecewiseCombiner class to allow hashing buffers piecewise. PiperOrigin-RevId: 278388902 GitOrigin-RevId: 074a799119ac881b8b8ce59ef7a3166d1aa025ac Change-Id: I61734850cbbb01c7585e8c736a5bb56e416512a8 --- absl/base/internal/low_level_alloc.cc | 4 +- absl/copts/GENERATED_AbseilCopts.cmake | 2 - absl/copts/GENERATED_copts.bzl | 2 - absl/copts/copts.py | 1 - absl/hash/BUILD.bazel | 2 +- absl/hash/hash_test.cc | 114 ++++++++++++++++- absl/hash/internal/hash.cc | 30 +++++ absl/hash/internal/hash.h | 121 +++++++++++++++++- absl/hash/internal/spy_hash_state.h | 13 ++ absl/strings/str_cat.h | 3 +- .../internal/create_thread_identity.cc | 1 - 11 files changed, 278 insertions(+), 15 deletions(-) diff --git a/absl/base/internal/low_level_alloc.cc b/absl/base/internal/low_level_alloc.cc index f7314ab1899..a0638f9ea49 100644 --- a/absl/base/internal/low_level_alloc.cc +++ b/absl/base/internal/low_level_alloc.cc @@ -447,7 +447,7 @@ static inline uintptr_t RoundUp(uintptr_t addr, uintptr_t align) { // that the freelist is in the correct order, that it // consists of regions marked "unallocated", and that no two regions // are adjacent in memory (they should have been coalesced). -// L < arena->mu +// L >= arena->mu static AllocList *Next(int i, AllocList *prev, LowLevelAlloc::Arena *arena) { ABSL_RAW_CHECK(i < prev->levels, "too few levels in Next()"); AllocList *next = prev->next[i]; @@ -508,8 +508,6 @@ void LowLevelAlloc::Free(void *v) { if (v != nullptr) { AllocList *f = reinterpret_cast( reinterpret_cast(v) - sizeof (f->header)); - ABSL_RAW_CHECK(f->header.magic == Magic(kMagicAllocated, &f->header), - "bad magic number in Free()"); LowLevelAlloc::Arena *arena = f->header.arena; ArenaLock section(arena); AddToFreelist(v, arena); diff --git a/absl/copts/GENERATED_AbseilCopts.cmake b/absl/copts/GENERATED_AbseilCopts.cmake index 39b79c53370..7ef6339be27 100644 --- a/absl/copts/GENERATED_AbseilCopts.cmake +++ b/absl/copts/GENERATED_AbseilCopts.cmake @@ -23,7 +23,6 @@ list(APPEND ABSL_CLANG_CL_FLAGS "-Wno-gcc-compat" "-Wno-global-constructors" "-Wno-exit-time-destructors" - "-Wno-nested-anon-types" "-Wno-non-modular-include-in-module" "-Wno-old-style-cast" "-Wno-range-loop-analysis" @@ -123,7 +122,6 @@ list(APPEND ABSL_LLVM_FLAGS "-Wno-gcc-compat" "-Wno-global-constructors" "-Wno-exit-time-destructors" - "-Wno-nested-anon-types" "-Wno-non-modular-include-in-module" "-Wno-old-style-cast" "-Wno-range-loop-analysis" diff --git a/absl/copts/GENERATED_copts.bzl b/absl/copts/GENERATED_copts.bzl index 7d645cc3374..3cc487845c6 100644 --- a/absl/copts/GENERATED_copts.bzl +++ b/absl/copts/GENERATED_copts.bzl @@ -24,7 +24,6 @@ ABSL_CLANG_CL_FLAGS = [ "-Wno-gcc-compat", "-Wno-global-constructors", "-Wno-exit-time-destructors", - "-Wno-nested-anon-types", "-Wno-non-modular-include-in-module", "-Wno-old-style-cast", "-Wno-range-loop-analysis", @@ -124,7 +123,6 @@ ABSL_LLVM_FLAGS = [ "-Wno-gcc-compat", "-Wno-global-constructors", "-Wno-exit-time-destructors", - "-Wno-nested-anon-types", "-Wno-non-modular-include-in-module", "-Wno-old-style-cast", "-Wno-range-loop-analysis", diff --git a/absl/copts/copts.py b/absl/copts/copts.py index a542541505b..704ef23450e 100644 --- a/absl/copts/copts.py +++ b/absl/copts/copts.py @@ -56,7 +56,6 @@ "-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 diff --git a/absl/hash/BUILD.bazel b/absl/hash/BUILD.bazel index c51248a5cb9..ffe8c294a0b 100644 --- a/absl/hash/BUILD.bazel +++ b/absl/hash/BUILD.bazel @@ -71,9 +71,9 @@ cc_test( deps = [ ":hash", ":hash_testing", + ":spy_hash_state", "//absl/base:core_headers", "//absl/container:flat_hash_set", - "//absl/hash:spy_hash_state", "//absl/meta:type_traits", "//absl/numeric:int128", "@com_google_googletest//:gtest_main", diff --git a/absl/hash/hash_test.cc b/absl/hash/hash_test.cc index 9a667ba703b..61f7661f591 100644 --- a/absl/hash/hash_test.cc +++ b/absl/hash/hash_test.cc @@ -274,8 +274,8 @@ TEST(HashValueTest, Strings) { const std::string small = "foo"; const std::string dup = "foofoo"; - const std::string large = "large"; - const std::string huge = std::string(5000, 'a'); + const std::string large = std::string(2048, 'x'); // multiple of chunk size + const std::string huge = std::string(5000, 'a'); // not a multiple EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple( std::string(), absl::string_view(), @@ -378,6 +378,116 @@ struct Private { } }; +// Test helper for combine_piecewise_buffer. It holds a string_view to the +// buffer-to-be-hashed. Its AbslHashValue specialization will split up its +// contents at the character offsets requested. +class PiecewiseHashTester { + public: + // Create a hash view of a buffer to be hashed contiguously. + explicit PiecewiseHashTester(absl::string_view buf) + : buf_(buf), piecewise_(false), split_locations_() {} + + // Create a hash view of a buffer to be hashed piecewise, with breaks at the + // given locations. + PiecewiseHashTester(absl::string_view buf, std::set split_locations) + : buf_(buf), + piecewise_(true), + split_locations_(std::move(split_locations)) {} + + template + friend H AbslHashValue(H h, const PiecewiseHashTester& p) { + if (!p.piecewise_) { + return H::combine_contiguous(std::move(h), p.buf_.data(), p.buf_.size()); + } + absl::hash_internal::PiecewiseCombiner combiner; + if (p.split_locations_.empty()) { + h = combiner.add_buffer(std::move(h), p.buf_.data(), p.buf_.size()); + return combiner.finalize(std::move(h)); + } + size_t begin = 0; + for (size_t next : p.split_locations_) { + absl::string_view chunk = p.buf_.substr(begin, next - begin); + h = combiner.add_buffer(std::move(h), chunk.data(), chunk.size()); + begin = next; + } + absl::string_view last_chunk = p.buf_.substr(begin); + if (!last_chunk.empty()) { + h = combiner.add_buffer(std::move(h), last_chunk.data(), + last_chunk.size()); + } + return combiner.finalize(std::move(h)); + } + + private: + absl::string_view buf_; + bool piecewise_; + std::set split_locations_; +}; + +// Dummy object that hashes as two distinct contiguous buffers, "foo" followed +// by "bar" +struct DummyFooBar { + template + friend H AbslHashValue(H h, const DummyFooBar&) { + const char* foo = "foo"; + const char* bar = "bar"; + h = H::combine_contiguous(std::move(h), foo, 3); + h = H::combine_contiguous(std::move(h), bar, 3); + return std::move(h); + } +}; + +TEST(HashValueTest, CombinePiecewiseBuffer) { + absl::Hash hash; + + // Check that hashing an empty buffer through the piecewise API works. + EXPECT_EQ(hash(PiecewiseHashTester("")), hash(PiecewiseHashTester("", {}))); + + // Similarly, small buffers should give consistent results + EXPECT_EQ(hash(PiecewiseHashTester("foobar")), + hash(PiecewiseHashTester("foobar", {}))); + EXPECT_EQ(hash(PiecewiseHashTester("foobar")), + hash(PiecewiseHashTester("foobar", {3}))); + + // But hashing "foobar" in pieces gives a different answer than hashing "foo" + // contiguously, then "bar" contiguously. + EXPECT_NE(hash(PiecewiseHashTester("foobar", {3})), + absl::Hash()(DummyFooBar{})); + + // Test hashing a large buffer incrementally, broken up in several different + // ways. Arrange for breaks on and near the stride boundaries to look for + // off-by-one errors in the implementation. + // + // This test is run on a buffer that is a multiple of the stride size, and one + // that isn't. + for (size_t big_buffer_size : {1024 * 2 + 512, 1024 * 3}) { + SCOPED_TRACE(big_buffer_size); + std::string big_buffer; + for (int i = 0; i < big_buffer_size; ++i) { + // Arbitrary std::string + big_buffer.push_back(32 + (i * (i / 3)) % 64); + } + auto big_buffer_hash = hash(PiecewiseHashTester(big_buffer)); + + const int possible_breaks = 9; + size_t breaks[possible_breaks] = {1, 512, 1023, 1024, 1025, + 1536, 2047, 2048, 2049}; + for (unsigned test_mask = 0; test_mask < (1u << possible_breaks); + ++test_mask) { + SCOPED_TRACE(test_mask); + std::set break_locations; + for (int j = 0; j < possible_breaks; ++j) { + if (test_mask & (1u << j)) { + break_locations.insert(breaks[j]); + } + } + EXPECT_EQ( + hash(PiecewiseHashTester(big_buffer, std::move(break_locations))), + big_buffer_hash); + } + } +} + TEST(HashValueTest, PrivateSanity) { // Sanity check that Private is working as the tests below expect it to work. EXPECT_TRUE(is_hashable::value); diff --git a/absl/hash/internal/hash.cc b/absl/hash/internal/hash.cc index 4ab7a9f8bfd..c17f3be1719 100644 --- a/absl/hash/internal/hash.cc +++ b/absl/hash/internal/hash.cc @@ -17,6 +17,36 @@ namespace absl { namespace hash_internal { +uint64_t CityHashState::CombineLargeContiguousImpl32(uint64_t state, + const unsigned char* first, + size_t len) { + while (len >= PiecewiseChunkSize()) { + state = + Mix(state, absl::hash_internal::CityHash32(reinterpret_cast(first), + PiecewiseChunkSize())); + len -= PiecewiseChunkSize(); + first += PiecewiseChunkSize(); + } + // Handle the remainder. + return CombineContiguousImpl(state, first, len, + std::integral_constant{}); +} + +uint64_t CityHashState::CombineLargeContiguousImpl64(uint64_t state, + const unsigned char* first, + size_t len) { + while (len >= PiecewiseChunkSize()) { + state = + Mix(state, absl::hash_internal::CityHash64(reinterpret_cast(first), + PiecewiseChunkSize())); + len -= PiecewiseChunkSize(); + first += PiecewiseChunkSize(); + } + // Handle the remainder. + return CombineContiguousImpl(state, first, len, + std::integral_constant{}); +} + ABSL_CONST_INIT const void* const CityHashState::kSeed = &kSeed; } // namespace hash_internal diff --git a/absl/hash/internal/hash.h b/absl/hash/internal/hash.h index 4ff4a126f0c..e99f8e75bd9 100644 --- a/absl/hash/internal/hash.h +++ b/absl/hash/internal/hash.h @@ -52,6 +52,12 @@ namespace absl { namespace hash_internal { +class PiecewiseCombiner; + +// Internal detail: Large buffers are hashed in smaller chunks. This function +// returns the size of these chunks. +constexpr int PiecewiseChunkSize() { return 1024; } + // HashStateBase // // A hash state object represents an intermediate state in the computation @@ -68,7 +74,7 @@ namespace hash_internal { // // `static H combine_contiguous(H state, const unsigned char*, size_t)`. // -// `HashStateBase` will provide a complete implementations for a hash state +// `HashStateBase` will provide a complete implementation for a hash state // object in terms of this method. // // Example: @@ -117,6 +123,9 @@ class HashStateBase { // for-loop instead. template static H combine_contiguous(H state, const T* data, size_t size); + + private: + friend class PiecewiseCombiner; }; // is_uniquely_represented @@ -187,6 +196,61 @@ H hash_bytes(H hash_state, const T& value) { return H::combine_contiguous(std::move(hash_state), start, sizeof(value)); } +// PiecewiseCombiner +// +// PiecewiseCombiner is an internal-only helper class for hashing a piecewise +// buffer of `char` or `unsigned char` as though it were contiguous. This class +// provides two methods: +// +// H add_buffer(state, data, size) +// H finalize(state) +// +// `add_buffer` can be called zero or more times, followed by a single call to +// `finalize`. This will produce the same hash expansion as concatenating each +// buffer piece into a single contiguous buffer, and passing this to +// `H::combine_contiguous`. +// +// Example usage: +// PiecewiseCombiner combiner; +// for (const auto& piece : pieces) { +// state = combiner.add_buffer(std::move(state), piece.data, piece.size); +// } +// return combiner.finalize(std::move(state)); +class PiecewiseCombiner { + public: + PiecewiseCombiner() : position_(0) {} + PiecewiseCombiner(const PiecewiseCombiner&) = delete; + PiecewiseCombiner& operator=(const PiecewiseCombiner&) = delete; + + // PiecewiseCombiner::add_buffer() + // + // Appends the given range of bytes to the sequence to be hashed, which may + // modify the provided hash state. + template + H add_buffer(H state, const unsigned char* data, size_t size); + template + H add_buffer(H state, const char* data, size_t size) { + return add_buffer(std::move(state), + reinterpret_cast(data), size); + } + + // PiecewiseCombiner::finalize() + // + // Finishes combining the hash sequence, which may may modify the provided + // hash state. + // + // Once finalize() is called, add_buffer() may no longer be called. The + // resulting hash state will be the same as if the pieces passed to + // add_buffer() were concatenated into a single flat buffer, and then provided + // to H::combine_contiguous(). + template + H finalize(H state); + + private: + unsigned char buf_[PiecewiseChunkSize()]; + size_t position_; +}; + // ----------------------------------------------------------------------------- // AbslHashValue for Basic Types // ----------------------------------------------------------------------------- @@ -709,6 +773,16 @@ class CityHashState : public HashStateBase { std::integral_constant /* sizeof_size_t*/); + // Slow dispatch path for calls to CombineContiguousImpl with a size argument + // larger than PiecewiseChunkSize(). Has the same effect as calling + // CombineContiguousImpl() repeatedly with the chunk stride size. + static uint64_t CombineLargeContiguousImpl32(uint64_t state, + const unsigned char* first, + size_t len); + static uint64_t CombineLargeContiguousImpl64(uint64_t state, + const unsigned char* first, + size_t len); + // Reads 9 to 16 bytes from p. // The first 8 bytes are in .first, the rest (zero padded) bytes are in // .second. @@ -776,6 +850,9 @@ inline uint64_t CityHashState::CombineContiguousImpl( // multiplicative hash. uint64_t v; if (len > 8) { + if (ABSL_PREDICT_FALSE(len > PiecewiseChunkSize())) { + return CombineLargeContiguousImpl32(state, first, len); + } v = absl::hash_internal::CityHash32(reinterpret_cast(first), len); } else if (len >= 4) { v = Read4To8(first, len); @@ -796,6 +873,9 @@ inline uint64_t CityHashState::CombineContiguousImpl( // multiplicative hash. uint64_t v; if (len > 16) { + if (ABSL_PREDICT_FALSE(len > PiecewiseChunkSize())) { + return CombineLargeContiguousImpl64(state, first, len); + } v = absl::hash_internal::CityHash64(reinterpret_cast(first), len); } else if (len > 8) { auto p = Read9To16(first, len); @@ -812,7 +892,6 @@ inline uint64_t CityHashState::CombineContiguousImpl( return Mix(state, v); } - struct AggregateBarrier {}; // HashImpl @@ -849,6 +928,44 @@ template H HashStateBase::combine_contiguous(H state, const T* data, size_t size) { return hash_internal::hash_range_or_bytes(std::move(state), data, size); } + +// HashStateBase::PiecewiseCombiner::add_buffer() +template +H PiecewiseCombiner::add_buffer(H state, const unsigned char* data, + size_t size) { + if (position_ + size < PiecewiseChunkSize()) { + // This partial chunk does not fill our existing buffer + memcpy(buf_ + position_, data, size); + position_ += size; + return std::move(state); + } + + // Complete the buffer and hash it + const size_t bytes_needed = PiecewiseChunkSize() - position_; + memcpy(buf_ + position_, data, bytes_needed); + state = H::combine_contiguous(std::move(state), buf_, PiecewiseChunkSize()); + data += bytes_needed; + size -= bytes_needed; + + // Hash whatever chunks we can without copying + while (size >= PiecewiseChunkSize()) { + state = H::combine_contiguous(std::move(state), data, PiecewiseChunkSize()); + data += PiecewiseChunkSize(); + size -= PiecewiseChunkSize(); + } + // Fill the buffer with the remainder + memcpy(buf_, data, size); + position_ = size; + return std::move(state); +} + +// HashStateBase::PiecewiseCombiner::finalize() +template +H PiecewiseCombiner::finalize(H state) { + // Hash the remainder left in the buffer, which may be empty + return H::combine_contiguous(std::move(state), buf_, position_); +} + } // namespace hash_internal } // namespace absl diff --git a/absl/hash/internal/spy_hash_state.h b/absl/hash/internal/spy_hash_state.h index c4cc8d07337..05c7cafeb0e 100644 --- a/absl/hash/internal/spy_hash_state.h +++ b/absl/hash/internal/spy_hash_state.h @@ -146,6 +146,19 @@ class SpyHashStateImpl : public HashStateBase> { static SpyHashStateImpl combine_contiguous(SpyHashStateImpl hash_state, const unsigned char* begin, size_t size) { + const size_t large_chunk_stride = PiecewiseChunkSize(); + if (size > large_chunk_stride) { + // Combining a large contiguous buffer must have the same effect as + // doing it piecewise by the stride length, followed by the (possibly + // empty) remainder. + while (size >= large_chunk_stride) { + hash_state = SpyHashStateImpl::combine_contiguous( + std::move(hash_state), begin, large_chunk_stride); + begin += large_chunk_stride; + size -= large_chunk_stride; + } + } + hash_state.hash_representation_.emplace_back( reinterpret_cast(begin), size); return hash_state; diff --git a/absl/strings/str_cat.h b/absl/strings/str_cat.h index a99aac01ebb..663a1945baa 100644 --- a/absl/strings/str_cat.h +++ b/absl/strings/str_cat.h @@ -290,7 +290,8 @@ class AlphaNum { // StrCat() // ----------------------------------------------------------------------------- // -// Merges given strings or numbers, using no delimiter(s). +// Merges given strings or numbers, using no delimiter(s), returning the merged +// result as a string. // // `StrCat()` is designed to be the fastest possible way to construct a string // out of a mix of raw C strings, string_views, strings, bool values, diff --git a/absl/synchronization/internal/create_thread_identity.cc b/absl/synchronization/internal/create_thread_identity.cc index eef661f23d1..ec49d1ca715 100644 --- a/absl/synchronization/internal/create_thread_identity.cc +++ b/absl/synchronization/internal/create_thread_identity.cc @@ -86,7 +86,6 @@ static void ResetThreadIdentity(base_internal::ThreadIdentity* identity) { pts->wake = false; pts->cond_waiter = false; pts->all_locks = nullptr; - identity->waiter_state = {}; identity->blocked_count_ptr = nullptr; identity->ticker.store(0, std::memory_order_relaxed); identity->wait_start.store(0, std::memory_order_relaxed); From 85092b4b648ca729c6263c4a302a41dfff28705e Mon Sep 17 00:00:00 2001 From: Adrian Ostrowski Date: Tue, 12 Nov 2019 21:17:48 +0100 Subject: [PATCH 0016/3164] Fix Conan builds (#400) Fix adding Conan setup invocation to CMakeLists.txt in conanfile.py --- conanfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index cd124aaee4b..926ec5ccd64 100644 --- a/conanfile.py +++ b/conanfile.py @@ -30,7 +30,7 @@ def configure(self): raise ConanInvalidConfiguration("Abseil does not support MSVC < 14") def build(self): - tools.replace_in_file("CMakeLists.txt", "project(absl)", "project(absl)\ninclude(conanbuildinfo.cmake)\nconan_basic_setup()") + tools.replace_in_file("CMakeLists.txt", "project(absl CXX)", "project(absl CXX)\ninclude(conanbuildinfo.cmake)\nconan_basic_setup()") cmake = CMake(self) cmake.definitions["BUILD_TESTING"] = False cmake.configure() From fa8c75182fbfdeddb2485fc0d53baeda3f40b7a3 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Wed, 13 Nov 2019 08:54:32 -0800 Subject: [PATCH 0017/3164] Export of internal Abseil changes -- 049ac45508e335c6f010f2d28d71016b9fa65b4e by Derek Mauro : Fix librt detection PiperOrigin-RevId: 280207723 -- 6382c3a9fb2643af9dc031f92ca846c4a78e249c by Andy Getzendanner : Fix Conan builds Import of https://github.com/abseil/abseil-cpp/pull/400 PiperOrigin-RevId: 280025424 -- aebcd52b1686ac82663a8d0193b60d0122a43372 by Samuel Benzaquen : Enable the assertion in the iterator's operator== and operator!= PiperOrigin-RevId: 279998951 -- 5b61d909e2159ac6fd45e0e456818db1e725ecd1 by Derek Mauro : Add best effort support for compiling much of Abseil with MinGW. This involves disabling ABSL_ATTRIBUTE_WEAK and adding link flags. A change to CCTZ is still necessary. Tests were not run yet, but most of them now build. PiperOrigin-RevId: 279966541 -- 4336f8b10cff906e2defdd7d1d449cde4907da5d by Abseil Team : Add comments and relax memory orders in base_internal::CallOnceImpl. Add a comment to document the memory order guarantee if base_internal::SpinLockWait() is called and returns kOnceDone. Add a comment for the load/store sequence in base_internal::CallOnceImpl based on Mike Burrows' explanation. The atomic load of 'control' in the #ifndef NDEBUG block does not need std::memory_order_acquire. It can use std::memory_order_relaxed. The atomic compare_exchange_strong of 'control' does not need std::memory_order_acquire in the success case. It can use std::memory_order_relaxed. PiperOrigin-RevId: 279814155 -- 407de3a5e9af957cded54a136ca0468bde620d4d by Abseil Team : Added a script to generate abseil.podspec from all BUILD.bazel files automatically. PiperOrigin-RevId: 279811441 -- 26139497d4a363d6c7bc989c554da593e8819a07 by Derek Mauro : Add missing copyright and Apache License to //absl/functional/BUILD.bazel PiperOrigin-RevId: 279795227 -- 98ed625b02af6e5834edf52a920d8ca2dab4cd90 by Matt Kulukundis : Switch the implementation of hashtablez to *only* work on platforms that have a PER_THREAD_TLS. The old case is very slow (global mutex) and nobody collects data from that configuration anyway. PiperOrigin-RevId: 279775149 -- 07225900ef672c005c38f467ad3f92f38d0922b3 by Derek Mauro : Remove the minumum glibc version check PiperOrigin-RevId: 279750412 -- ec09956a951b4f52228ecc81968b8db7ae19ed15 by Derek Mauro : CMake only: link with -lrt to support older glibc versions PiperOrigin-RevId: 279741661 -- 97b113fb2e8246f6152c36330ba13793b37154b6 by Xiaoyi Zhang : Internal change. PiperOrigin-RevId: 279390188 -- ca8f72f2721546cc9b01bd01b2ea144962e6e0c5 by Andy Getzendanner : Expose PutTwoDigits for internal use within Abseil. PiperOrigin-RevId: 279374239 -- 14c6384cc03bbdfdefd2e4b635f104af5dd7e026 by Derek Mauro : Remove log_severity sources from the base target. They are already compiled as part of a separate library. PiperOrigin-RevId: 279372619 -- 3c5d926c718f8bf394e3bee87b6ba8d94601e0d3 by Abseil Team : s/indepdent/independent/g in SimpleAtof's documentation. PiperOrigin-RevId: 279350836 -- de2c44be8a8edf9efa1fe2007cba3564f3e5b0b8 by Abseil Team : Internal change PiperOrigin-RevId: 279346990 -- 2ba078341423fcf6d0ba5ca1831f86570a26e615 by Samuel Benzaquen : Add hash support for std::wstring, std::u16string and std::u32string. PiperOrigin-RevId: 279320672 -- 3272d3ffcfa55283a04f90e5868701912da95ef7 by Andy Soffer : Removing a bunch of __restricts that amount to no performance differences. One of these is the cause of https://github.com/abseil/abseil-cpp/issues/396. In particular, in one of the Vector128Store functions, restricts on two pointers that were indeed aliased seems to be the root cause of the issues. Closes #396 PiperOrigin-RevId: 279318999 -- 342f338ab31cc24344d5de8f28cf455bbb629a17 by Jorg Brown : Support uint128 in SimpleAtoi PiperOrigin-RevId: 279234038 -- 81cb0a04cf2dc4515d303679fc60968712191571 by Derek Mauro : Change the check for futex availability to support older Linux systems PiperOrigin-RevId: 279147079 -- cb4ca4aa4c8d2d710a5d483c56c4ce4f979e14b1 by Abseil Team : Add IWYU pragma: export for int128 .inc files. PiperOrigin-RevId: 279107098 -- b8df86ef610c366729f07326c726f3e34817b4dd by Abseil Team : An optimization for Waiter::Post() in the SEM waiter mode. Like the FUTEX waiter mode, Waiter::Post() only needs to call Poke() if it incremented the atomic variable from 0. PiperOrigin-RevId: 279086133 GitOrigin-RevId: 049ac45508e335c6f010f2d28d71016b9fa65b4e Change-Id: I4c1a4073fff62cb6a1fcb1c104aa7d62dad588c2 --- absl/abseil.podspec.gen.py | 247 ++++++++++++++++++ absl/base/BUILD.bazel | 4 +- absl/base/CMakeLists.txt | 8 +- absl/base/attributes.h | 6 +- absl/base/call_once.h | 13 +- absl/base/policy_checks.h | 10 - absl/container/internal/hashtablez_sampler.cc | 21 +- absl/container/internal/hashtablez_sampler.h | 10 +- .../internal/hashtablez_sampler_test.cc | 2 + absl/container/internal/raw_hash_set.h | 4 +- absl/container/internal/raw_hash_set_test.cc | 2 + absl/debugging/BUILD.bazel | 5 +- absl/debugging/CMakeLists.txt | 1 + absl/functional/BUILD.bazel | 16 ++ absl/hash/hash_test.cc | 27 ++ absl/hash/internal/hash.h | 13 + absl/numeric/int128.h | 4 +- absl/random/CMakeLists.txt | 1 + absl/random/internal/BUILD.bazel | 5 +- absl/random/internal/randen_hwaes.cc | 48 ++-- absl/strings/internal/numbers_test_common.h | 5 +- absl/strings/numbers.cc | 66 ++--- absl/strings/numbers.h | 21 +- absl/strings/numbers_test.cc | 63 ++++- absl/synchronization/internal/waiter.cc | 7 +- absl/synchronization/internal/waiter.h | 11 +- 26 files changed, 503 insertions(+), 117 deletions(-) create mode 100755 absl/abseil.podspec.gen.py diff --git a/absl/abseil.podspec.gen.py b/absl/abseil.podspec.gen.py new file mode 100755 index 00000000000..2bf153c002a --- /dev/null +++ b/absl/abseil.podspec.gen.py @@ -0,0 +1,247 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +"""This script generates abseil.podspec from all BUILD.bazel files. + +This is expected to run on abseil git repository with Bazel 1.0 on Linux. +It recursively analyzes BUILD.bazel files using query command of Bazel to +dump its build rules in XML format. From these rules, it constructs podspec +structure. +""" + +import argparse +import collections +import os +import re +import subprocess +import xml.etree.ElementTree + +# Template of root podspec. +SPEC_TEMPLATE = """ +# This file has been automatically generated from a script. +# Please make modifications to `abseil.podspec.gen.py` instead. +Pod::Spec.new do |s| + s.name = 'abseil' + s.version = '${version}' + s.summary = 'Abseil Common Libraries (C++) from Google' + s.homepage = 'https://abseil.io' + s.license = 'Apache License, Version 2.0' + s.authors = { 'Abseil Team' => 'abseil-io@googlegroups.com' } + s.source = { + :git => 'https://github.com/abseil/abseil-cpp.git', + :tag => '${tag}', + } + s.module_name = 'absl' + s.header_mappings_dir = 'absl' + s.header_dir = 'absl' + s.libraries = 'c++' + s.compiler_flags = '-Wno-everything' + s.pod_target_xcconfig = { + 'USER_HEADER_SEARCH_PATHS' => '$(inherited) "$(PODS_TARGET_SRCROOT)"', + 'USE_HEADERMAP' => 'NO', + 'ALWAYS_SEARCH_USER_PATHS' => 'NO', + } + s.ios.deployment_target = '7.0' + s.osx.deployment_target = '10.9' + s.tvos.deployment_target = '9.0' + s.watchos.deployment_target = '2.0' +""" + +# Limited platforms that abseil supports. +# This is mainly because of sigaltstack unavailable on watchOS. +LIMITED_SUPPORT_PLATFORMS = [ + "ios.deployment_target = '7.0'", + "osx.deployment_target = '10.9'", +] + +# Custom specification per rule. +CUSTOM_SPEC_MAP = { + "//absl/debugging:failure_signal_handler": LIMITED_SUPPORT_PLATFORMS, +} + +# Rule object representing the rule of Bazel BUILD. +Rule = collections.namedtuple( + "Rule", "type name package srcs hdrs textual_hdrs deps visibility testonly") + + +def get_elem_value(elem, name): + """Returns the value of XML element with the given name.""" + for child in elem: + if child.attrib.get("name") != name: + continue + if child.tag == "string": + return child.attrib.get("value") + if child.tag == "boolean": + return child.attrib.get("value") == "true" + if child.tag == "list": + return [nested_child.attrib.get("value") for nested_child in child] + raise "Cannot recognize tag: " + child.tag + return None + + +def normalize_paths(paths): + """Returns the list of normalized path.""" + # e.g. ["//absl/strings:dir/header.h"] -> ["absl/strings/dir/header.h"] + return [path.lstrip("/").replace(":", "/") for path in paths] + + +def parse_rule(elem, package): + """Returns a rule from bazel XML rule.""" + return Rule( + type=elem.attrib["class"], + name=get_elem_value(elem, "name"), + package=package, + srcs=normalize_paths(get_elem_value(elem, "srcs") or []), + hdrs=normalize_paths(get_elem_value(elem, "hdrs") or []), + textual_hdrs=normalize_paths(get_elem_value(elem, "textual_hdrs") or []), + deps=get_elem_value(elem, "deps") or [], + visibility=get_elem_value(elem, "visibility") or [], + testonly=get_elem_value(elem, "testonly") or False) + + +def read_build(package): + """Runs bazel query on given package file and returns all cc rules.""" + result = subprocess.check_output( + ["bazel", "query", package + ":all", "--output", "xml"]) + root = xml.etree.ElementTree.fromstring(result) + return [ + parse_rule(elem, package) + for elem in root + if elem.tag == "rule" and elem.attrib["class"].startswith("cc_") + ] + + +def collect_rules(root_path): + """Collects and returns all rules from root path recursively.""" + rules = [] + for cur, _, _ in os.walk(root_path): + build_path = os.path.join(cur, "BUILD.bazel") + if os.path.exists(build_path): + rules.extend(read_build("//" + cur)) + return rules + + +def relevant_rule(rule): + """Returns true if a given rule is relevant when generating a podspec.""" + return ( + # cc_library only (ignore cc_test, cc_binary) + rule.type == "cc_library" and + # ignore empty rule + (rule.hdrs + rule.textual_hdrs + rule.srcs) and + # ignore test-only rule + not rule.testonly) + + +def get_spec_var(depth): + """Returns the name of variable for spec with given depth.""" + return "s" if depth == 0 else "s{}".format(depth) + + +def get_spec_name(label): + """Converts the label of bazel rule to the name of podspec.""" + assert label.startswith("//absl/"), "{} doesn't start with //absl/".format( + label) + # e.g. //absl/apple/banana -> abseil/apple/banana + return "abseil/" + label[7:] + + +def write_podspec(f, rules, args): + """Writes a podspec from given rules and args.""" + rule_dir = build_rule_directory(rules)["abseil"] + # Write root part with given arguments + spec = re.sub(r"\$\{(\w+)\}", lambda x: args[x.group(1)], + SPEC_TEMPLATE).lstrip() + f.write(spec) + # Write all target rules + write_podspec_map(f, rule_dir, 0) + f.write("end\n") + + +def build_rule_directory(rules): + """Builds a tree-style rule directory from given rules.""" + rule_dir = {} + for rule in rules: + cur = rule_dir + for frag in get_spec_name(rule.package).split("/"): + cur = cur.setdefault(frag, {}) + cur[rule.name] = rule + return rule_dir + + +def write_podspec_map(f, cur_map, depth): + """Writes podspec from rule map recursively.""" + for key, value in sorted(cur_map.items()): + indent = " " * (depth + 1) + f.write("{indent}{var0}.subspec '{key}' do |{var1}|\n".format( + indent=indent, + key=key, + var0=get_spec_var(depth), + var1=get_spec_var(depth + 1))) + if isinstance(value, dict): + write_podspec_map(f, value, depth + 1) + else: + write_podspec_rule(f, value, depth + 1) + f.write("{indent}end\n".format(indent=indent)) + + +def write_podspec_rule(f, rule, depth): + """Writes podspec from given rule.""" + indent = " " * (depth + 1) + spec_var = get_spec_var(depth) + # Puts all files in hdrs, textual_hdrs, and srcs into source_files. + # Since CocoaPods treats header_files a bit differently from bazel, + # this won't generate a header_files field so that all source_files + # are considered as header files. + srcs = sorted(set(rule.hdrs + rule.textual_hdrs + rule.srcs)) + write_indented_list( + f, "{indent}{var}.source_files = ".format(indent=indent, var=spec_var), + srcs) + # Writes dependencies of this rule. + for dep in sorted(rule.deps): + name = get_spec_name(dep.replace(":", "/")) + f.write("{indent}{var}.dependency '{dep}'\n".format( + indent=indent, var=spec_var, dep=name)) + # Writes custom specification. + custom_spec = CUSTOM_SPEC_MAP.get(rule.package + ":" + rule.name) + if custom_spec: + for spec in custom_spec: + f.write("{indent}{var}.{spec}\n".format( + indent=indent, var=spec_var, spec=spec)) + + +def write_indented_list(f, leading, values): + """Writes leading values in an indented style.""" + f.write(leading) + f.write((",\n" + " " * len(leading)).join("'{}'".format(v) for v in values)) + f.write("\n") + + +def generate(args): + """Generates a podspec file from all BUILD files under absl directory.""" + rules = filter(relevant_rule, collect_rules("absl")) + with open(args.output, "wt") as f: + write_podspec(f, rules, vars(args)) + + +def main(): + parser = argparse.ArgumentParser( + description="Generates abseil.podspec from BUILD.bazel") + parser.add_argument( + "-v", "--version", help="The version of podspec", required=True) + parser.add_argument( + "-t", + "--tag", + default=None, + help="The name of git tag (default: version)") + parser.add_argument( + "-o", + "--output", + default="abseil.podspec", + help="The name of output file (default: abseil.podspec)") + args = parser.parse_args() + if args.tag is None: + args.tag = args.version + generate(args) + + +if __name__ == "__main__": + main() diff --git a/absl/base/BUILD.bazel b/absl/base/BUILD.bazel index 6fc712d968f..7e234bc6677 100644 --- a/absl/base/BUILD.bazel +++ b/absl/base/BUILD.bazel @@ -192,7 +192,9 @@ cc_library( ], copts = ABSL_DEFAULT_COPTS, linkopts = select({ - "//absl:windows": [], + "//absl:windows": [ + "-DEFAULTLIB:shlwapi.lib", + ], "//conditions:default": ["-pthread"], }) + ABSL_DEFAULT_LINKOPTS, deps = [ diff --git a/absl/base/CMakeLists.txt b/absl/base/CMakeLists.txt index 9550cdb2db4..7ab6955e217 100644 --- a/absl/base/CMakeLists.txt +++ b/absl/base/CMakeLists.txt @@ -14,6 +14,8 @@ # limitations under the License. # +find_library(LIBRT rt) + absl_cc_library( NAME atomic_hook @@ -163,16 +165,18 @@ absl_cc_library( "internal/thread_identity.h" "internal/tsan_mutex_interface.h" "internal/unscaledcycleclock.h" - "log_severity.h" SRCS "internal/cycleclock.cc" "internal/spinlock.cc" "internal/sysinfo.cc" "internal/thread_identity.cc" "internal/unscaledcycleclock.cc" - "log_severity.cc" COPTS ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + $<$:${LIBRT}> + $<$:"shlwapi"> DEPS absl::atomic_hook absl::base_internal diff --git a/absl/base/attributes.h b/absl/base/attributes.h index 7b7656a859b..acd1c526982 100644 --- a/absl/base/attributes.h +++ b/absl/base/attributes.h @@ -158,9 +158,11 @@ // Weak attributes currently do not work properly in LLVM's Windows backend, // so disable them there. See https://bugs.llvm.org/show_bug.cgi?id=37598 // for further information. -#if (ABSL_HAVE_ATTRIBUTE(weak) || \ +// The MinGW compiler doesn't complain about the weak attribute until the link +// step, presumably because Windows doesn't use ELF binaries. +#if (ABSL_HAVE_ATTRIBUTE(weak) || \ (defined(__GNUC__) && !defined(__clang__))) && \ - !(defined(__llvm__) && defined(_WIN32)) + !(defined(__llvm__) && defined(_WIN32)) && !defined(__MINGW32__) #undef ABSL_ATTRIBUTE_WEAK #define ABSL_ATTRIBUTE_WEAK __attribute__((weak)) #define ABSL_HAVE_ATTRIBUTE_WEAK 1 diff --git a/absl/base/call_once.h b/absl/base/call_once.h index 4aa6360cffc..e1614e517a8 100644 --- a/absl/base/call_once.h +++ b/absl/base/call_once.h @@ -148,7 +148,7 @@ void CallOnceImpl(std::atomic* control, Args&&... args) { #ifndef NDEBUG { - uint32_t old_control = control->load(std::memory_order_acquire); + uint32_t old_control = control->load(std::memory_order_relaxed); if (old_control != kOnceInit && old_control != kOnceRunning && old_control != kOnceWaiter && @@ -166,14 +166,23 @@ void CallOnceImpl(std::atomic* control, // Must do this before potentially modifying control word's state. base_internal::SchedulingHelper maybe_disable_scheduling(scheduling_mode); // Short circuit the simplest case to avoid procedure call overhead. + // The base_internal::SpinLockWait() call returns either kOnceInit or + // kOnceDone. If it returns kOnceDone, it must have loaded the control word + // with std::memory_order_acquire and seen a value of kOnceDone. uint32_t old_control = kOnceInit; if (control->compare_exchange_strong(old_control, kOnceRunning, - std::memory_order_acquire, std::memory_order_relaxed) || base_internal::SpinLockWait(control, ABSL_ARRAYSIZE(trans), trans, scheduling_mode) == kOnceInit) { base_internal::Invoke(std::forward(fn), std::forward(args)...); + // The call to SpinLockWake below is an optimization, because the waiter + // in SpinLockWait is waiting with a short timeout. The atomic load/store + // sequence is slightly faster than an atomic exchange: + // old_control = control->exchange(base_internal::kOnceDone, + // std::memory_order_release); + // We opt for a slightly faster case when there are no waiters, in spite + // of longer tail latency when there are waiters. old_control = control->load(std::memory_order_relaxed); control->store(base_internal::kOnceDone, std::memory_order_release); if (old_control == base_internal::kOnceWaiter) { diff --git a/absl/base/policy_checks.h b/absl/base/policy_checks.h index 699fb1a2e0f..4dfa49e54ac 100644 --- a/absl/base/policy_checks.h +++ b/absl/base/policy_checks.h @@ -82,16 +82,6 @@ // Standard Library Check // ----------------------------------------------------------------------------- -// We have chosen glibc 2.12 as the minimum as it was tagged for release -// in May, 2010 and includes some functionality used in Google software -// (for instance pthread_setname_np): -// https://sourceware.org/ml/libc-alpha/2010-05/msg00000.html -#if defined(__GLIBC__) && defined(__GLIBC_PREREQ) -#if !__GLIBC_PREREQ(2, 12) -#error "Minimum required version of glibc is 2.12." -#endif -#endif - #if defined(_STLPORT_VERSION) #error "STLPort is not supported." #endif diff --git a/absl/container/internal/hashtablez_sampler.cc b/absl/container/internal/hashtablez_sampler.cc index 0a7ef61c8fe..4aec0a07ffb 100644 --- a/absl/container/internal/hashtablez_sampler.cc +++ b/absl/container/internal/hashtablez_sampler.cc @@ -38,16 +38,18 @@ ABSL_CONST_INIT std::atomic g_hashtablez_enabled{ ABSL_CONST_INIT std::atomic g_hashtablez_sample_parameter{1 << 10}; ABSL_CONST_INIT std::atomic g_hashtablez_max_samples{1 << 20}; -#if ABSL_HAVE_THREAD_LOCAL -thread_local absl::base_internal::ExponentialBiased - g_exponential_biased_generator; -#else -ABSL_CONST_INIT static absl::base_internal::ExponentialBiased +#if ABSL_PER_THREAD_TLS == 1 +ABSL_PER_THREAD_TLS_KEYWORD absl::base_internal::ExponentialBiased g_exponential_biased_generator; #endif } // namespace +#if ABSL_PER_THREAD_TLS == 1 +ABSL_PER_THREAD_TLS_KEYWORD int64_t global_next_sample = 0; +#endif // ABSL_PER_THREAD_TLS == 1 + + HashtablezSampler& HashtablezSampler::Global() { static auto* sampler = new HashtablezSampler(); return *sampler; @@ -189,6 +191,10 @@ HashtablezInfo* SampleSlow(int64_t* next_sample) { return HashtablezSampler::Global().Register(); } +#if ABSL_PER_THREAD_TLS == 0 + *next_sample = std::numeric_limits::max(); + return nullptr; +#else bool first = *next_sample < 0; *next_sample = g_exponential_biased_generator.Get( g_hashtablez_sample_parameter.load(std::memory_order_relaxed)); @@ -210,12 +216,9 @@ HashtablezInfo* SampleSlow(int64_t* next_sample) { } return HashtablezSampler::Global().Register(); +#endif } -#if ABSL_PER_THREAD_TLS == 1 -ABSL_PER_THREAD_TLS_KEYWORD int64_t global_next_sample = 0; -#endif // ABSL_PER_THREAD_TLS == 1 - void UnsampleSlow(HashtablezInfo* info) { HashtablezSampler::Global().Unregister(info); } diff --git a/absl/container/internal/hashtablez_sampler.h b/absl/container/internal/hashtablez_sampler.h index 53996bb9fa4..c694df057d8 100644 --- a/absl/container/internal/hashtablez_sampler.h +++ b/absl/container/internal/hashtablez_sampler.h @@ -186,16 +186,14 @@ extern ABSL_PER_THREAD_TLS_KEYWORD int64_t global_next_sample; // 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_PER_THREAD_TLS == 1 if (ABSL_PREDICT_TRUE(--global_next_sample > 0)) { return HashtablezInfoHandle(nullptr); } return HashtablezInfoHandle(SampleSlow(&global_next_sample)); +#else + return HashtablezInfoHandle(nullptr); +#endif // !ABSL_PER_THREAD_TLS } // Holds samples and their associated stack traces with a soft limit of diff --git a/absl/container/internal/hashtablez_sampler_test.cc b/absl/container/internal/hashtablez_sampler_test.cc index 7f9e8dd21ff..af4b5ee108a 100644 --- a/absl/container/internal/hashtablez_sampler_test.cc +++ b/absl/container/internal/hashtablez_sampler_test.cc @@ -168,6 +168,7 @@ TEST(HashtablezInfoTest, RecordRehash) { EXPECT_EQ(info.num_erases.load(), 0); } +#if ABSL_PER_THREAD_TLS == 1 TEST(HashtablezSamplerTest, SmallSampleParameter) { SetHashtablezEnabled(true); SetHashtablezSampleParameter(100); @@ -211,6 +212,7 @@ TEST(HashtablezSamplerTest, Sample) { } EXPECT_NEAR(sample_rate, 0.01, 0.005); } +#endif TEST(HashtablezSamplerTest, Handle) { auto& sampler = HashtablezSampler::Global(); diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index bf0c03c44f2..469226fedd8 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h @@ -638,8 +638,8 @@ class raw_hash_set { } friend bool operator==(const iterator& a, const iterator& b) { - /* To be enabled: a.assert_is_valid(); */ - /* To be enabled: b.assert_is_valid(); */ + a.assert_is_valid(); + b.assert_is_valid(); return a.ctrl_ == b.ctrl_; } friend bool operator!=(const iterator& a, const iterator& b) { diff --git a/absl/container/internal/raw_hash_set_test.cc b/absl/container/internal/raw_hash_set_test.cc index 33cfa72ce7a..ec8f92313ba 100644 --- a/absl/container/internal/raw_hash_set_test.cc +++ b/absl/container/internal/raw_hash_set_test.cc @@ -1841,6 +1841,7 @@ TEST(TableDeathTest, EraseOfEndAsserts) { EXPECT_DEATH_IF_SUPPORTED(t.erase(t.end()), kDeathMsg); } +#if ABSL_PER_THREAD_TLS == 1 TEST(RawHashSamplerTest, Sample) { // Enable the feature even if the prod default is off. SetHashtablezEnabled(true); @@ -1861,6 +1862,7 @@ TEST(RawHashSamplerTest, Sample) { EXPECT_NEAR((end_size - start_size) / static_cast(tables.size()), 0.01, 0.005); } +#endif TEST(RawHashSamplerTest, DoNotSampleCustomAllocators) { // Enable the feature even if the prod default is off. diff --git a/absl/debugging/BUILD.bazel b/absl/debugging/BUILD.bazel index beec279ff8e..2601090bc60 100644 --- a/absl/debugging/BUILD.bazel +++ b/absl/debugging/BUILD.bazel @@ -55,7 +55,10 @@ cc_library( "symbolize.h", ], copts = ABSL_DEFAULT_COPTS, - linkopts = ABSL_DEFAULT_LINKOPTS, + linkopts = ABSL_DEFAULT_LINKOPTS + select({ + "//absl:windows": ["-DEFAULTLIB:dbghelp.lib"], + "//conditions:default": [], + }), deps = [ ":debugging_internal", ":demangle_internal", diff --git a/absl/debugging/CMakeLists.txt b/absl/debugging/CMakeLists.txt index b3e35b6d744..eff504b4883 100644 --- a/absl/debugging/CMakeLists.txt +++ b/absl/debugging/CMakeLists.txt @@ -44,6 +44,7 @@ absl_cc_library( ${ABSL_DEFAULT_COPTS} LINKOPTS ${ABSL_DEFAULT_LINKOPTS} + $<$:"dbghelp"> DEPS absl::debugging_internal absl::demangle_internal diff --git a/absl/functional/BUILD.bazel b/absl/functional/BUILD.bazel index e861b8a5601..0a7b588e70e 100644 --- a/absl/functional/BUILD.bazel +++ b/absl/functional/BUILD.bazel @@ -1,3 +1,19 @@ +# +# Copyright 2019 The Abseil Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") load( "//absl:copts/configure_copts.bzl", diff --git a/absl/hash/hash_test.cc b/absl/hash/hash_test.cc index 61f7661f591..42c0c3d8bf7 100644 --- a/absl/hash/hash_test.cc +++ b/absl/hash/hash_test.cc @@ -300,6 +300,33 @@ TEST(HashValueTest, Strings) { SpyHash(absl::string_view("ABC"))); } +TEST(HashValueTest, WString) { + EXPECT_TRUE((is_hashable::value)); + + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple( + std::wstring(), std::wstring(L"ABC"), std::wstring(L"ABC"), + std::wstring(L"Some other different string"), + std::wstring(L"Iñtërnâtiônàlizætiøn")))); +} + +TEST(HashValueTest, U16String) { + EXPECT_TRUE((is_hashable::value)); + + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple( + std::u16string(), std::u16string(u"ABC"), std::u16string(u"ABC"), + std::u16string(u"Some other different string"), + std::u16string(u"Iñtërnâtiônàlizætiøn")))); +} + +TEST(HashValueTest, U32String) { + EXPECT_TRUE((is_hashable::value)); + + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple( + std::u32string(), std::u32string(U"ABC"), std::u32string(U"ABC"), + std::u32string(U"Some other different string"), + std::u32string(U"Iñtërnâtiônàlizætiøn")))); +} + TEST(HashValueTest, StdArray) { EXPECT_TRUE((is_hashable>::value)); diff --git a/absl/hash/internal/hash.h b/absl/hash/internal/hash.h index e99f8e75bd9..7d44f57dc90 100644 --- a/absl/hash/internal/hash.h +++ b/absl/hash/internal/hash.h @@ -427,6 +427,19 @@ H AbslHashValue(H hash_state, absl::string_view str) { str.size()); } +// Support std::wstring, std::u16string and std::u32string. +template ::value || + std::is_same::value || + std::is_same::value>> +H AbslHashValue( + H hash_state, + const std::basic_string, Alloc>& str) { + return H::combine( + H::combine_contiguous(std::move(hash_state), str.data(), str.size()), + str.size()); +} + // ----------------------------------------------------------------------------- // AbslHashValue for Sequence Containers // ----------------------------------------------------------------------------- diff --git a/absl/numeric/int128.h b/absl/numeric/int128.h index 10be8eca42e..4f965686c39 100644 --- a/absl/numeric/int128.h +++ b/absl/numeric/int128.h @@ -720,9 +720,9 @@ inline uint128& uint128::operator--() { } #if defined(ABSL_HAVE_INTRINSIC_INT128) -#include "absl/numeric/int128_have_intrinsic.inc" +#include "absl/numeric/int128_have_intrinsic.inc" // IWYU pragma: export #else // ABSL_HAVE_INTRINSIC_INT128 -#include "absl/numeric/int128_no_intrinsic.inc" +#include "absl/numeric/int128_no_intrinsic.inc" // IWYU pragma: export #endif // ABSL_HAVE_INTRINSIC_INT128 } // namespace absl diff --git a/absl/random/CMakeLists.txt b/absl/random/CMakeLists.txt index 289854ffd40..264a6f3c6ab 100644 --- a/absl/random/CMakeLists.txt +++ b/absl/random/CMakeLists.txt @@ -447,6 +447,7 @@ absl_cc_library( ${ABSL_DEFAULT_COPTS} LINKOPTS ${ABSL_DEFAULT_LINKOPTS} + $<$:"bcrypt"> DEPS absl::core_headers absl::optional diff --git a/absl/random/internal/BUILD.bazel b/absl/random/internal/BUILD.bazel index 91388d19f3c..cc9bc013bc9 100644 --- a/absl/random/internal/BUILD.bazel +++ b/absl/random/internal/BUILD.bazel @@ -89,7 +89,10 @@ cc_library( "seed_material.h", ], copts = ABSL_DEFAULT_COPTS, - linkopts = ABSL_DEFAULT_LINKOPTS, + linkopts = ABSL_DEFAULT_LINKOPTS + select({ + "//absl:windows": ["-DEFAULTLIB:bcrypt.lib"], + "//conditions:default": [], + }), deps = [ ":fast_uniform_bits", "//absl/base:core_headers", diff --git a/absl/random/internal/randen_hwaes.cc b/absl/random/internal/randen_hwaes.cc index 7d5b2b74128..6cc36fd39b5 100644 --- a/absl/random/internal/randen_hwaes.cc +++ b/absl/random/internal/randen_hwaes.cc @@ -159,13 +159,11 @@ inline ABSL_TARGET_CRYPTO Vector128 ReverseBytes(const Vector128& v) { // WARNING: these load/store in native byte order. It is OK to load and then // store an unchanged vector, but interpreting the bits as a number or input // to AES will have undefined results. -inline ABSL_TARGET_CRYPTO Vector128 -Vector128Load(const void* ABSL_RANDOM_INTERNAL_RESTRICT from) { +inline ABSL_TARGET_CRYPTO Vector128 Vector128Load(const void* from) { return vec_vsx_ld(0, reinterpret_cast(from)); } -inline ABSL_TARGET_CRYPTO void Vector128Store( - const Vector128& v, void* ABSL_RANDOM_INTERNAL_RESTRICT to) { +inline ABSL_TARGET_CRYPTO void Vector128Store(const Vector128& v, void* to) { vec_vsx_st(v, 0, reinterpret_cast(to)); } @@ -177,8 +175,7 @@ inline ABSL_TARGET_CRYPTO Vector128 AesRound(const Vector128& state, } // Enables native loads in the round loop by pre-swapping. -inline ABSL_TARGET_CRYPTO void SwapEndian( - uint64_t* ABSL_RANDOM_INTERNAL_RESTRICT state) { +inline ABSL_TARGET_CRYPTO void SwapEndian(uint64_t* state) { using absl::random_internal::RandenTraits; constexpr size_t kLanes = 2; constexpr size_t kFeistelBlocks = RandenTraits::kFeistelBlocks; @@ -230,13 +227,11 @@ using Vector128 = uint8x16_t; namespace { -inline ABSL_TARGET_CRYPTO Vector128 -Vector128Load(const void* ABSL_RANDOM_INTERNAL_RESTRICT from) { +inline ABSL_TARGET_CRYPTO Vector128 Vector128Load(const void* from) { return vld1q_u8(reinterpret_cast(from)); } -inline ABSL_TARGET_CRYPTO void Vector128Store( - const Vector128& v, void* ABSL_RANDOM_INTERNAL_RESTRICT to) { +inline ABSL_TARGET_CRYPTO void Vector128Store(const Vector128& v, void* to) { vst1q_u8(reinterpret_cast(to), v); } @@ -254,8 +249,7 @@ inline ABSL_TARGET_CRYPTO Vector128 AesRound(const Vector128& state, return vaesmcq_u8(vaeseq_u8(state, uint8x16_t{})) ^ round_key; } -inline ABSL_TARGET_CRYPTO void SwapEndian( - uint64_t* ABSL_RANDOM_INTERNAL_RESTRICT) {} +inline ABSL_TARGET_CRYPTO void SwapEndian(uint64_t*) {} } // namespace @@ -283,15 +277,12 @@ class Vector128 { __m128i data_; }; -inline ABSL_TARGET_CRYPTO Vector128 -Vector128Load(const void* ABSL_RANDOM_INTERNAL_RESTRICT from) { +inline ABSL_TARGET_CRYPTO Vector128 Vector128Load(const void* from) { return Vector128(_mm_load_si128(reinterpret_cast(from))); } -inline ABSL_TARGET_CRYPTO void Vector128Store( - const Vector128& v, void* ABSL_RANDOM_INTERNAL_RESTRICT to) { - _mm_store_si128(reinterpret_cast<__m128i * ABSL_RANDOM_INTERNAL_RESTRICT>(to), - v.data()); +inline ABSL_TARGET_CRYPTO void Vector128Store(const Vector128& v, void* to) { + _mm_store_si128(reinterpret_cast<__m128i*>(to), v.data()); } // One round of AES. "round_key" is a public constant for breaking the @@ -304,8 +295,7 @@ inline ABSL_TARGET_CRYPTO Vector128 AesRound(const Vector128& state, return Vector128(_mm_aesenc_si128(state.data(), round_key.data())); } -inline ABSL_TARGET_CRYPTO void SwapEndian( - uint64_t* ABSL_RANDOM_INTERNAL_RESTRICT) {} +inline ABSL_TARGET_CRYPTO void SwapEndian(uint64_t*) {} } // namespace @@ -402,8 +392,7 @@ constexpr size_t kLanes = 2; // Block shuffles applies a shuffle to the entire state between AES rounds. // Improved odd-even shuffle from "New criterion for diffusion property". -inline ABSL_TARGET_CRYPTO void BlockShuffle( - uint64_t* ABSL_RANDOM_INTERNAL_RESTRICT state) { +inline ABSL_TARGET_CRYPTO void BlockShuffle(uint64_t* state) { static_assert(kFeistelBlocks == 16, "Expecting 16 FeistelBlocks."); constexpr size_t shuffle[kFeistelBlocks] = {7, 2, 13, 4, 11, 8, 3, 6, @@ -452,8 +441,7 @@ inline ABSL_TARGET_CRYPTO void BlockShuffle( // parallel hides the 7-cycle AESNI latency on HSW. Note that the Feistel // XORs are 'free' (included in the second AES instruction). inline ABSL_TARGET_CRYPTO const u64x2* FeistelRound( - uint64_t* ABSL_RANDOM_INTERNAL_RESTRICT state, - const u64x2* ABSL_RANDOM_INTERNAL_RESTRICT keys) { + uint64_t* state, const u64x2* ABSL_RANDOM_INTERNAL_RESTRICT keys) { static_assert(kFeistelBlocks == 16, "Expecting 16 FeistelBlocks."); // MSVC does a horrible job at unrolling loops. @@ -513,8 +501,7 @@ inline ABSL_TARGET_CRYPTO const u64x2* FeistelRound( // 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_TARGET_CRYPTO void Permute( - const void* ABSL_RANDOM_INTERNAL_RESTRICT keys, - uint64_t* ABSL_RANDOM_INTERNAL_RESTRICT state) { + const void* ABSL_RANDOM_INTERNAL_RESTRICT keys, uint64_t* state) { const u64x2* ABSL_RANDOM_INTERNAL_RESTRICT keys128 = static_cast(keys); @@ -544,10 +531,8 @@ const void* ABSL_TARGET_CRYPTO RandenHwAes::GetKeys() { // NOLINTNEXTLINE void ABSL_TARGET_CRYPTO RandenHwAes::Absorb(const void* seed_void, void* state_void) { - uint64_t* ABSL_RANDOM_INTERNAL_RESTRICT state = - reinterpret_cast(state_void); - const uint64_t* ABSL_RANDOM_INTERNAL_RESTRICT seed = - reinterpret_cast(seed_void); + auto* state = static_cast(state_void); + const auto* seed = static_cast(seed_void); constexpr size_t kCapacityBlocks = kCapacityBytes / sizeof(Vector128); constexpr size_t kStateBlocks = kStateBytes / sizeof(Vector128); @@ -623,8 +608,7 @@ void ABSL_TARGET_CRYPTO RandenHwAes::Generate(const void* keys, void* state_void) { static_assert(kCapacityBytes == sizeof(Vector128), "Capacity mismatch"); - uint64_t* ABSL_RANDOM_INTERNAL_RESTRICT state = - reinterpret_cast(state_void); + auto* state = static_cast(state_void); const Vector128 prev_inner = Vector128Load(state); diff --git a/absl/strings/internal/numbers_test_common.h b/absl/strings/internal/numbers_test_common.h index 28247205af4..a263219e27b 100644 --- a/absl/strings/internal/numbers_test_common.h +++ b/absl/strings/internal/numbers_test_common.h @@ -42,8 +42,9 @@ inline bool Itoa(IntType value, int base, std::string* destination) { while (value != 0) { const IntType next_value = value / base; // Can't use std::abs here because of problems when IntType is unsigned. - int remainder = value > next_value * base ? value - next_value * base - : next_value * base - value; + int remainder = + static_cast(value > next_value * base ? value - next_value * base + : next_value * base - value); char c = remainder < 10 ? '0' + remainder : 'A' + remainder - 10; destination->insert(0, 1, c); value = next_value; diff --git a/absl/strings/numbers.cc b/absl/strings/numbers.cc index d7b94fc1301..4890bd54a54 100644 --- a/absl/strings/numbers.cc +++ b/absl/strings/numbers.cc @@ -93,43 +93,6 @@ bool SimpleAtod(absl::string_view str, double* out) { return true; } -namespace { - -// Writes a two-character representation of 'i' to 'buf'. 'i' must be in the -// range 0 <= i < 100, and buf must have space for two characters. Example: -// char buf[2]; -// PutTwoDigits(42, buf); -// // buf[0] == '4' -// // buf[1] == '2' -inline void PutTwoDigits(size_t i, char* buf) { - static const char two_ASCII_digits[100][2] = { - {'0', '0'}, {'0', '1'}, {'0', '2'}, {'0', '3'}, {'0', '4'}, - {'0', '5'}, {'0', '6'}, {'0', '7'}, {'0', '8'}, {'0', '9'}, - {'1', '0'}, {'1', '1'}, {'1', '2'}, {'1', '3'}, {'1', '4'}, - {'1', '5'}, {'1', '6'}, {'1', '7'}, {'1', '8'}, {'1', '9'}, - {'2', '0'}, {'2', '1'}, {'2', '2'}, {'2', '3'}, {'2', '4'}, - {'2', '5'}, {'2', '6'}, {'2', '7'}, {'2', '8'}, {'2', '9'}, - {'3', '0'}, {'3', '1'}, {'3', '2'}, {'3', '3'}, {'3', '4'}, - {'3', '5'}, {'3', '6'}, {'3', '7'}, {'3', '8'}, {'3', '9'}, - {'4', '0'}, {'4', '1'}, {'4', '2'}, {'4', '3'}, {'4', '4'}, - {'4', '5'}, {'4', '6'}, {'4', '7'}, {'4', '8'}, {'4', '9'}, - {'5', '0'}, {'5', '1'}, {'5', '2'}, {'5', '3'}, {'5', '4'}, - {'5', '5'}, {'5', '6'}, {'5', '7'}, {'5', '8'}, {'5', '9'}, - {'6', '0'}, {'6', '1'}, {'6', '2'}, {'6', '3'}, {'6', '4'}, - {'6', '5'}, {'6', '6'}, {'6', '7'}, {'6', '8'}, {'6', '9'}, - {'7', '0'}, {'7', '1'}, {'7', '2'}, {'7', '3'}, {'7', '4'}, - {'7', '5'}, {'7', '6'}, {'7', '7'}, {'7', '8'}, {'7', '9'}, - {'8', '0'}, {'8', '1'}, {'8', '2'}, {'8', '3'}, {'8', '4'}, - {'8', '5'}, {'8', '6'}, {'8', '7'}, {'8', '8'}, {'8', '9'}, - {'9', '0'}, {'9', '1'}, {'9', '2'}, {'9', '3'}, {'9', '4'}, - {'9', '5'}, {'9', '6'}, {'9', '7'}, {'9', '8'}, {'9', '9'} - }; - assert(i < 100); - memcpy(buf, two_ASCII_digits[i], 2); -} - -} // namespace - bool SimpleAtob(absl::string_view str, bool* out) { ABSL_RAW_CHECK(out != nullptr, "Output pointer must not be nullptr."); if (EqualsIgnoreCase(str, "true") || EqualsIgnoreCase(str, "t") || @@ -496,13 +459,13 @@ static ExpDigits SplitToSix(const double value) { int two_digits = dddddd / 10000; dddddd -= two_digits * 10000; - PutTwoDigits(two_digits, &exp_dig.digits[0]); + numbers_internal::PutTwoDigits(two_digits, &exp_dig.digits[0]); two_digits = dddddd / 100; dddddd -= two_digits * 100; - PutTwoDigits(two_digits, &exp_dig.digits[2]); + numbers_internal::PutTwoDigits(two_digits, &exp_dig.digits[2]); - PutTwoDigits(dddddd, &exp_dig.digits[4]); + numbers_internal::PutTwoDigits(dddddd, &exp_dig.digits[4]); return exp_dig; } @@ -908,6 +871,25 @@ ABSL_CONST_INIT const char kHexTable[513] = "e0e1e2e3e4e5e6e7e8e9eaebecedeeef" "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"; +ABSL_CONST_INIT const char two_ASCII_digits[100][2] = { + {'0', '0'}, {'0', '1'}, {'0', '2'}, {'0', '3'}, {'0', '4'}, {'0', '5'}, + {'0', '6'}, {'0', '7'}, {'0', '8'}, {'0', '9'}, {'1', '0'}, {'1', '1'}, + {'1', '2'}, {'1', '3'}, {'1', '4'}, {'1', '5'}, {'1', '6'}, {'1', '7'}, + {'1', '8'}, {'1', '9'}, {'2', '0'}, {'2', '1'}, {'2', '2'}, {'2', '3'}, + {'2', '4'}, {'2', '5'}, {'2', '6'}, {'2', '7'}, {'2', '8'}, {'2', '9'}, + {'3', '0'}, {'3', '1'}, {'3', '2'}, {'3', '3'}, {'3', '4'}, {'3', '5'}, + {'3', '6'}, {'3', '7'}, {'3', '8'}, {'3', '9'}, {'4', '0'}, {'4', '1'}, + {'4', '2'}, {'4', '3'}, {'4', '4'}, {'4', '5'}, {'4', '6'}, {'4', '7'}, + {'4', '8'}, {'4', '9'}, {'5', '0'}, {'5', '1'}, {'5', '2'}, {'5', '3'}, + {'5', '4'}, {'5', '5'}, {'5', '6'}, {'5', '7'}, {'5', '8'}, {'5', '9'}, + {'6', '0'}, {'6', '1'}, {'6', '2'}, {'6', '3'}, {'6', '4'}, {'6', '5'}, + {'6', '6'}, {'6', '7'}, {'6', '8'}, {'6', '9'}, {'7', '0'}, {'7', '1'}, + {'7', '2'}, {'7', '3'}, {'7', '4'}, {'7', '5'}, {'7', '6'}, {'7', '7'}, + {'7', '8'}, {'7', '9'}, {'8', '0'}, {'8', '1'}, {'8', '2'}, {'8', '3'}, + {'8', '4'}, {'8', '5'}, {'8', '6'}, {'8', '7'}, {'8', '8'}, {'8', '9'}, + {'9', '0'}, {'9', '1'}, {'9', '2'}, {'9', '3'}, {'9', '4'}, {'9', '5'}, + {'9', '6'}, {'9', '7'}, {'9', '8'}, {'9', '9'}}; + bool safe_strto32_base(absl::string_view text, int32_t* value, int base) { return safe_int_internal(text, value, base); } @@ -924,5 +906,9 @@ bool safe_strtou64_base(absl::string_view text, uint64_t* value, int base) { return safe_uint_internal(text, value, base); } +bool safe_strtou128_base(absl::string_view text, uint128* value, int base) { + return safe_uint_internal(text, value, base); +} + } // namespace numbers_internal } // namespace absl diff --git a/absl/strings/numbers.h b/absl/strings/numbers.h index 9b8ec89a894..5e15ca40b73 100644 --- a/absl/strings/numbers.h +++ b/absl/strings/numbers.h @@ -67,7 +67,7 @@ ABSL_MUST_USE_RESULT bool SimpleAtoi(absl::string_view str, int_type* out); // Converts the given string (optionally followed or preceded by ASCII // whitespace) into a float, which may be rounded on overflow or underflow. // See https://en.cppreference.com/w/c/string/byte/strtof for details about the -// allowed formats for `str`, except SimpleAtof() is locale-indepdent and will +// allowed formats for `str`, except SimpleAtof() is locale-independent and will // always use the "C" locale. If any errors are encountered, this function // returns `false`, leaving `out` in an unspecified state. ABSL_MUST_USE_RESULT bool SimpleAtof(absl::string_view str, float* out); @@ -102,12 +102,26 @@ namespace numbers_internal { // Digit conversion. extern const char kHexChar[17]; // 0123456789abcdef extern const char kHexTable[513]; // 000102030405060708090a0b0c0d0e0f1011... +extern const char two_ASCII_digits[100][2]; // 00, 01, 02, 03... + +// Writes a two-character representation of 'i' to 'buf'. 'i' must be in the +// range 0 <= i < 100, and buf must have space for two characters. Example: +// char buf[2]; +// PutTwoDigits(42, buf); +// // buf[0] == '4' +// // buf[1] == '2' +inline void PutTwoDigits(size_t i, char* buf) { + assert(i < 100); + memcpy(buf, two_ASCII_digits[i], 2); +} // safe_strto?() functions for implementing SimpleAtoi() bool safe_strto32_base(absl::string_view text, int32_t* value, int base); bool safe_strto64_base(absl::string_view text, int64_t* value, int base); bool safe_strtou32_base(absl::string_view text, uint32_t* value, int base); bool safe_strtou64_base(absl::string_view text, uint64_t* value, int base); +bool safe_strtou128_base(absl::string_view text, absl::uint128* value, + int base); static const int kFastToBufferSize = 32; static const int kSixDigitsToBufferSize = 16; @@ -232,6 +246,11 @@ ABSL_MUST_USE_RESULT bool SimpleAtoi(absl::string_view str, int_type* out) { return numbers_internal::safe_strtoi_base(str, out, 10); } +ABSL_MUST_USE_RESULT inline bool SimpleAtoi(absl::string_view str, + absl::uint128* out) { + return numbers_internal::safe_strtou128_base(str, out, 10); +} + } // namespace absl #endif // ABSL_STRINGS_NUMBERS_H_ diff --git a/absl/strings/numbers_test.cc b/absl/strings/numbers_test.cc index b92b9a8cb2a..68229b15a4e 100644 --- a/absl/strings/numbers_test.cc +++ b/absl/strings/numbers_test.cc @@ -249,7 +249,9 @@ TEST(Numbers, TestFastPrints) { template void VerifySimpleAtoiGood(in_val_type in_value, int_type exp_value) { - std::string s = absl::StrCat(in_value); + std::string s; + // uint128 can be streamed but not StrCat'd + absl::strings_internal::OStringStream(&s) << in_value; int_type x = static_cast(~exp_value); EXPECT_TRUE(SimpleAtoi(s, &x)) << "in_value=" << in_value << " s=" << s << " x=" << x; @@ -325,6 +327,25 @@ TEST(NumbersTest, Atoi) { VerifySimpleAtoiGood(std::numeric_limits::max(), std::numeric_limits::max()); + // SimpleAtoi(absl::string_view, absl::uint128) + VerifySimpleAtoiGood(0, 0); + VerifySimpleAtoiGood(42, 42); + VerifySimpleAtoiBad(-42); + + VerifySimpleAtoiBad(std::numeric_limits::min()); + VerifySimpleAtoiGood(std::numeric_limits::max(), + std::numeric_limits::max()); + VerifySimpleAtoiGood(std::numeric_limits::max(), + std::numeric_limits::max()); + VerifySimpleAtoiBad(std::numeric_limits::min()); + VerifySimpleAtoiGood(std::numeric_limits::max(), + std::numeric_limits::max()); + VerifySimpleAtoiGood(std::numeric_limits::max(), + std::numeric_limits::max()); + VerifySimpleAtoiGood( + std::numeric_limits::max(), + std::numeric_limits::max()); + // Some other types VerifySimpleAtoiGood(-42, -42); VerifySimpleAtoiGood(-42, -42); @@ -657,6 +678,46 @@ TEST(stringtest, safe_strtou32_random) { TEST(stringtest, safe_strtou64_random) { test_random_integer_parse_base(&safe_strtou64_base); } +TEST(stringtest, safe_strtou128_random) { + // random number generators don't work for uint128, and + // uint128 can be streamed but not StrCat'd, so this code must be custom + // implemented for uint128, but is generally the same as what's above. + // test_random_integer_parse_base( + // &absl::numbers_internal::safe_strtou128_base); + using RandomEngine = std::minstd_rand0; + using IntType = absl::uint128; + constexpr auto parse_func = &absl::numbers_internal::safe_strtou128_base; + + std::random_device rd; + RandomEngine rng(rd()); + std::uniform_int_distribution random_uint64( + std::numeric_limits::min()); + std::uniform_int_distribution random_base(2, 35); + + for (size_t i = 0; i < kNumRandomTests; i++) { + IntType value = random_uint64(rng); + value = (value << 64) + random_uint64(rng); + int base = random_base(rng); + std::string str_value; + EXPECT_TRUE(Itoa(value, base, &str_value)); + IntType parsed_value; + + // Test successful parse + EXPECT_TRUE(parse_func(str_value, &parsed_value, base)); + EXPECT_EQ(parsed_value, value); + + // Test overflow + std::string s; + absl::strings_internal::OStringStream(&s) + << std::numeric_limits::max() << value; + EXPECT_FALSE(parse_func(s, &parsed_value, base)); + + // Test underflow + s.clear(); + absl::strings_internal::OStringStream(&s) << "-" << value; + EXPECT_FALSE(parse_func(s, &parsed_value, base)); + } +} TEST(stringtest, safe_strtou32_base) { for (int i = 0; strtouint32_test_cases()[i].str != nullptr; ++i) { diff --git a/absl/synchronization/internal/waiter.cc b/absl/synchronization/internal/waiter.cc index 8e713770532..f1425872e5b 100644 --- a/absl/synchronization/internal/waiter.cc +++ b/absl/synchronization/internal/waiter.cc @@ -342,8 +342,11 @@ bool Waiter::Wait(KernelTimeout t) { } void Waiter::Post() { - wakeups_.fetch_add(1, std::memory_order_release); // Post a wakeup. - Poke(); + // Post a wakeup. + if (wakeups_.fetch_add(1, std::memory_order_release) == 0) { + // We incremented from 0, need to wake a potential waiter. + Poke(); + } } void Waiter::Poke() { diff --git a/absl/synchronization/internal/waiter.h b/absl/synchronization/internal/waiter.h index cac81d5fd22..9540598be5d 100644 --- a/absl/synchronization/internal/waiter.h +++ b/absl/synchronization/internal/waiter.h @@ -24,6 +24,10 @@ #include #endif +#ifdef __linux__ +#include +#endif + #ifdef ABSL_HAVE_SEMAPHORE_H #include #endif @@ -44,7 +48,12 @@ #define ABSL_WAITER_MODE ABSL_FORCE_WAITER_MODE #elif defined(_WIN32) && _WIN32_WINNT >= _WIN32_WINNT_VISTA #define ABSL_WAITER_MODE ABSL_WAITER_MODE_WIN32 -#elif defined(__linux__) +#elif defined(__BIONIC__) +// Bionic supports all the futex operations we need even when some of the futex +// definitions are missing. +#define ABSL_WAITER_MODE ABSL_WAITER_MODE_FUTEX +#elif defined(__linux__) && defined(FUTEX_CLOCK_REALTIME) +// FUTEX_CLOCK_REALTIME requires Linux >= 2.6.28. #define ABSL_WAITER_MODE ABSL_WAITER_MODE_FUTEX #elif defined(ABSL_HAVE_SEMAPHORE_H) #define ABSL_WAITER_MODE ABSL_WAITER_MODE_SEM From 3df7b52a6ada51a72a23796b844549a7b282f1b8 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Fri, 15 Nov 2019 08:07:12 -0800 Subject: [PATCH 0018/3164] Export of internal Abseil changes -- 3a871e2cd854a46770a9416189953b2b38980bcf by Andy Getzendanner : Import of CCTZ from GitHub. PiperOrigin-RevId: 280660534 -- f5a155fb056dcf68b089c21aebb5bf5159dc0eb3 by Tom Manshreck : Nit: fix format of algorithm.h comments PiperOrigin-RevId: 280474139 -- 08b117a18353c0daf51b33b45100e5a007e5ab0c by Abseil Team : Allow absl::Notification::HasBeenNotified to be inlined. PiperOrigin-RevId: 280453271 -- 26d33dd2bad72945f1987f7b2669dbec3a6247e6 by Derek Mauro : Fix unused variable warnings on Windows PiperOrigin-RevId: 280324417 -- 907e9b23029be683af3b443bafd6a577a595922f by Abseil Team : PeriodicSampler implementation (for internal-use only) PiperOrigin-RevId: 280257168 GitOrigin-RevId: 3a871e2cd854a46770a9416189953b2b38980bcf Change-Id: I6e2978cae22a6adc41c0577e5c21355e9db7c8f5 --- absl/algorithm/algorithm.h | 11 +- absl/base/BUILD.bazel | 41 ++++ absl/base/CMakeLists.txt | 27 +++ absl/base/internal/periodic_sampler.cc | 50 +++++ absl/base/internal/periodic_sampler.h | 193 ++++++++++++++++++ .../internal/periodic_sampler_benchmark.cc | 70 +++++++ absl/base/internal/periodic_sampler_test.cc | 175 ++++++++++++++++ .../internal/stacktrace_win32-inl.inc | 2 +- absl/debugging/symbolize_win32.inc | 6 +- absl/synchronization/notification.cc | 9 - absl/synchronization/notification.h | 9 +- .../internal/cctz/src/zone_info_source.cc | 7 +- 12 files changed, 581 insertions(+), 19 deletions(-) create mode 100644 absl/base/internal/periodic_sampler.cc create mode 100644 absl/base/internal/periodic_sampler.h create mode 100644 absl/base/internal/periodic_sampler_benchmark.cc create mode 100644 absl/base/internal/periodic_sampler_test.cc diff --git a/absl/algorithm/algorithm.h b/absl/algorithm/algorithm.h index bb90215d80d..771228a0201 100644 --- a/absl/algorithm/algorithm.h +++ b/absl/algorithm/algorithm.h @@ -84,6 +84,8 @@ It RotateImpl(It first, It middle, It last, std::false_type) { } // namespace algorithm_internal +// equal() +// // Compares the equality of two ranges specified by pairs of iterators, using // the given predicate, returning true iff for each corresponding iterator i1 // and i2 in the first and second range respectively, pred(*i1, *i2) == true @@ -104,8 +106,8 @@ bool equal(InputIter1 first1, InputIter1 last1, InputIter2 first2, typename std::iterator_traits::iterator_category{}); } -// Performs comparison of two ranges specified by pairs of iterators using -// operator==. +// Overload of equal() that performs comparison of two ranges specified by pairs +// of iterators using operator==. template bool equal(InputIter1 first1, InputIter1 last1, InputIter2 first2, InputIter2 last2) { @@ -113,6 +115,8 @@ bool equal(InputIter1 first1, InputIter1 last1, InputIter2 first2, algorithm_internal::EqualTo{}); } +// linear_search() +// // Performs a linear search for `value` using the iterator `first` up to // but not including `last`, returning true if [`first`, `last`) contains an // element equal to `value`. @@ -126,6 +130,8 @@ bool linear_search(InputIterator first, InputIterator last, return std::find(first, last, value) != last; } +// rotate() +// // Performs a left rotation on a range of elements (`first`, `last`) such that // `middle` is now the first element. `rotate()` returns an iterator pointing to // the first element before rotation. This function is exactly the same as @@ -135,7 +141,6 @@ bool linear_search(InputIterator first, InputIterator last, // The complexity of this algorithm is the same as that of `std::rotate`, but if // `ForwardIterator` is not a random-access iterator, then `absl::rotate` // performs an additional pass over the range to construct the return value. - template ForwardIterator rotate(ForwardIterator first, ForwardIterator middle, ForwardIterator last) { diff --git a/absl/base/BUILD.bazel b/absl/base/BUILD.bazel index 7e234bc6677..f6b210f41e3 100644 --- a/absl/base/BUILD.bazel +++ b/absl/base/BUILD.bazel @@ -585,6 +585,47 @@ cc_test( ], ) +cc_library( + name = "periodic_sampler", + srcs = ["internal/periodic_sampler.cc"], + hdrs = ["internal/periodic_sampler.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":core_headers", + ":exponential_biased", + ], +) + +cc_test( + name = "periodic_sampler_test", + size = "small", + srcs = ["internal/periodic_sampler_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = ["//visibility:private"], + deps = [ + ":core_headers", + ":periodic_sampler", + "@com_google_googletest//:gtest_main", + ], +) + +cc_binary( + name = "periodic_sampler_benchmark", + testonly = 1, + srcs = ["internal/periodic_sampler_benchmark.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + tags = ["benchmark"], + visibility = ["//visibility:private"], + deps = [ + ":core_headers", + ":periodic_sampler", + "@com_github_google_benchmark//:benchmark_main", + ], +) + cc_library( name = "scoped_set_env", testonly = 1, diff --git a/absl/base/CMakeLists.txt b/absl/base/CMakeLists.txt index 7ab6955e217..9b0174d80ef 100644 --- a/absl/base/CMakeLists.txt +++ b/absl/base/CMakeLists.txt @@ -534,6 +534,33 @@ absl_cc_test( gmock_main ) +absl_cc_library( + NAME + periodic_sampler + SRCS + "internal/periodic_sampler.cc" + HDRS + "internal/periodic_sampler.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::core_headers + absl::exponential_biased +) + +absl_cc_test( + NAME + periodic_sampler_test + SRCS + "internal/periodic_sampler_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::core_headers + absl::periodic_sampler + gmock_main +) + absl_cc_library( NAME scoped_set_env diff --git a/absl/base/internal/periodic_sampler.cc b/absl/base/internal/periodic_sampler.cc new file mode 100644 index 00000000000..439745d0c51 --- /dev/null +++ b/absl/base/internal/periodic_sampler.cc @@ -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. + +#include "absl/base/internal/periodic_sampler.h" + +#include + +#include "absl/base/internal/exponential_biased.h" + +namespace absl { +namespace base_internal { + +int64_t PeriodicSamplerBase::GetExponentialBiased(int period) noexcept { + return rng_.Get(period); +} + +bool PeriodicSamplerBase::SubtleConfirmSample() noexcept { + int current_period = period(); + + // Deal with period case 0 (always off) and 1 (always on) + if (ABSL_PREDICT_FALSE(current_period < 2)) { + stride_ = 0; + return current_period == 1; + } + + // Check if this is the first call to Sample() + if (ABSL_PREDICT_FALSE(stride_ == 1)) { + stride_ = -1 - GetExponentialBiased(current_period); + if (stride_ < -1) { + ++stride_; + return false; + } + } + stride_ = -1 - GetExponentialBiased(current_period); + return true; +} + +} // namespace base_internal +} // namespace absl diff --git a/absl/base/internal/periodic_sampler.h b/absl/base/internal/periodic_sampler.h new file mode 100644 index 00000000000..2c0600f05ea --- /dev/null +++ b/absl/base/internal/periodic_sampler.h @@ -0,0 +1,193 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_BASE_INTERNAL_PERIODIC_SAMPLER_H_ +#define ABSL_BASE_INTERNAL_PERIODIC_SAMPLER_H_ + +#include + +#include + +#include "absl/base/internal/exponential_biased.h" +#include "absl/base/optimization.h" + +namespace absl { +namespace base_internal { + +// PeriodicSamplerBase provides the basic period sampler implementation. +// +// This is the base class for the templated PeriodicSampler class, which holds +// a global std::atomic value identified by a user defined tag, such that +// each specific PeriodSampler implementation holds its own global period. +// +// PeriodicSamplerBase is thread-compatible except where stated otherwise. +class PeriodicSamplerBase { + public: + // PeriodicSamplerBase is trivial / copyable / movable / destructible. + PeriodicSamplerBase() = default; + PeriodicSamplerBase(PeriodicSamplerBase&&) = default; + PeriodicSamplerBase(const PeriodicSamplerBase&) = default; + + // Returns true roughly once every `period` calls. This is established by a + // randomly picked `stride` that is counted down on each call to `Sample`. + // This stride is picked such that the probability of `Sample()` returning + // true is 1 in `period`. + inline bool Sample() noexcept; + + // The below methods are intended for optimized use cases where the + // size of the inlined fast path code is highly important. Applications + // should use the `Sample()` method unless they have proof that their + // specific use case requires the optimizations offered by these methods. + // + // An example of such a use case is SwissTable sampling. All sampling checks + // are in inlined SwissTable methods, and the number of call sites is huge. + // In this case, the inlined code size added to each translation unit calling + // SwissTable methods is non-trivial. + // + // The `SubtleMaybeSample()` function spuriously returns true even if the + // function should not be sampled, applications MUST match each call to + // 'SubtleMaybeSample()' returning true with a `SubtleConfirmSample()` call, + // and use the result of the latter as the sampling decision. + // In other words: the code should logically be equivalent to: + // + // if (SubtleMaybeSample() && SubtleConfirmSample()) { + // // Sample this call + // } + // + // In the 'inline-size' optimized case, the `SubtleConfirmSample()` call can + // be placed out of line, for example, the typical use case looks as follows: + // + // // --- frobber.h ----------- + // void FrobberSampled(); + // + // inline void FrobberImpl() { + // // ... + // } + // + // inline void Frobber() { + // if (ABSL_PREDICT_FALSE(sampler.SubtleMaybeSample())) { + // FrobberSampled(); + // } else { + // FrobberImpl(); + // } + // } + // + // // --- frobber.cc ----------- + // void FrobberSampled() { + // if (!sampler.SubtleConfirmSample())) { + // // Spurious false positive + // FrobberImpl(); + // return; + // } + // + // // Sampled execution + // // ... + // } + inline bool SubtleMaybeSample() noexcept; + bool SubtleConfirmSample() noexcept; + + protected: + // We explicitly don't use a virtual destructor as this class is never + // virtually destroyed, and it keeps the class trivial, which avoids TLS + // prologue and epilogue code for our TLS instances. + ~PeriodicSamplerBase() = default; + + // Returns the next stride for our sampler. + // This function is virtual for testing purposes only. + virtual int64_t GetExponentialBiased(int period) noexcept; + + private: + // Returns the current period of this sampler. Thread-safe. + virtual int period() const noexcept = 0; + + int64_t stride_ = 0; + ExponentialBiased rng_; +}; + +inline bool PeriodicSamplerBase::SubtleMaybeSample() noexcept { + // We explicitly count up and not down, as the compiler does not generate + // ideal code for counting down. See also https://gcc.godbolt.org/z/FTPDSM + // + // With `if (ABSL_PREDICT_FALSE(++stride_ < 0))` + // add QWORD PTR fs:sampler@tpoff+8, 1 + // jns .L15 + // ret + // + // With `if (ABSL_PREDICT_FALSE(--stride_ > 0))` + // mov rax, QWORD PTR fs:sampler@tpoff+8 + // sub rax, 1 + // mov QWORD PTR fs:sampler@tpoff+8, rax + // test rax, rax + // jle .L15 + // ret + // add QWORD PTR fs:sampler@tpoff+8, 1 + // jns .L15 + // ret + // + // --stride >= 0 does work, but makes our logic slightly harder as in that + // case we have less convenient zero-init and over-run values. + if (ABSL_PREDICT_FALSE(++stride_ < 0)) { + return false; + } + return true; +} + +inline bool PeriodicSamplerBase::Sample() noexcept { + return ABSL_PREDICT_FALSE(SubtleMaybeSample()) ? SubtleConfirmSample() + : false; +} + +// PeriodicSampler is a concreted periodic sampler implementation. +// The user provided Tag identifies the implementation, and is required to +// isolate the global state of this instance from other instances. +// +// Typical use case: +// +// struct HashTablezTag {}; +// thread_local PeriodicSampler sampler; +// +// void HashTableSamplingLogic(...) { +// if (sampler.Sample()) { +// HashTableSlowSamplePath(...); +// } +// } +// +template +class PeriodicSampler final : public PeriodicSamplerBase { + public: + ~PeriodicSampler() = default; + + int period() const noexcept final { + return period_.load(std::memory_order_relaxed); + } + + // Sets the global period for this sampler. Thread-safe. + // Setting a period of 0 disables the sampler, i.e., every call to Sample() + // will return false. Setting a period of 1 puts the sampler in 'always on' + // mode, i.e., every call to Sample() returns true. + static void SetGlobalPeriod(int period) { + period_.store(period, std::memory_order_relaxed); + } + + private: + static std::atomic period_; +}; + +template +std::atomic PeriodicSampler::period_(default_period); + +} // namespace base_internal +} // namespace absl + +#endif // ABSL_BASE_INTERNAL_PERIODIC_SAMPLER_H_ diff --git a/absl/base/internal/periodic_sampler_benchmark.cc b/absl/base/internal/periodic_sampler_benchmark.cc new file mode 100644 index 00000000000..f87cef73dc1 --- /dev/null +++ b/absl/base/internal/periodic_sampler_benchmark.cc @@ -0,0 +1,70 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "benchmark/benchmark.h" +#include "absl/base/internal/periodic_sampler.h" + +namespace absl { +namespace base_internal { +namespace { + +template +void BM_Sample(Sampler* sampler, benchmark::State& state) { + for (auto _ : state) { + benchmark::DoNotOptimize(sampler); + benchmark::DoNotOptimize(sampler->Sample()); + } +} + +template +void BM_SampleMinunumInlined(Sampler* sampler, benchmark::State& state) { + for (auto _ : state) { + benchmark::DoNotOptimize(sampler); + if (ABSL_PREDICT_FALSE(sampler->SubtleMaybeSample())) { + benchmark::DoNotOptimize(sampler->SubtleConfirmSample()); + } + } +} + +void BM_PeriodicSampler_ShortSample(benchmark::State& state) { + struct Tag {}; + PeriodicSampler sampler; + BM_Sample(&sampler, state); +} +BENCHMARK(BM_PeriodicSampler_ShortSample); + +void BM_PeriodicSampler_LongSample(benchmark::State& state) { + struct Tag {}; + PeriodicSampler sampler; + BM_Sample(&sampler, state); +} +BENCHMARK(BM_PeriodicSampler_LongSample); + +void BM_PeriodicSampler_LongSampleMinunumInlined(benchmark::State& state) { + struct Tag {}; + PeriodicSampler sampler; + BM_SampleMinunumInlined(&sampler, state); +} +BENCHMARK(BM_PeriodicSampler_LongSampleMinunumInlined); + +void BM_PeriodicSampler_Disabled(benchmark::State& state) { + struct Tag {}; + PeriodicSampler sampler; + BM_Sample(&sampler, state); +} +BENCHMARK(BM_PeriodicSampler_Disabled); + +} // namespace +} // namespace base_internal +} // namespace absl diff --git a/absl/base/internal/periodic_sampler_test.cc b/absl/base/internal/periodic_sampler_test.cc new file mode 100644 index 00000000000..1ba61377a0f --- /dev/null +++ b/absl/base/internal/periodic_sampler_test.cc @@ -0,0 +1,175 @@ +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/base/internal/periodic_sampler.h" + +#include // NOLINT(build/c++11) + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/attributes.h" +#include "absl/base/macros.h" + +namespace absl { +namespace base_internal { +namespace { + +using testing::Eq; +using testing::Return; +using testing::StrictMock; + +class MockPeriodicSampler : public PeriodicSamplerBase { + public: + virtual ~MockPeriodicSampler() = default; + + MOCK_METHOD(int, period, (), (const, noexcept)); + MOCK_METHOD(int64_t, GetExponentialBiased, (int), (noexcept)); +}; + +TEST(PeriodicSamplerBaseTest, Sample) { + StrictMock sampler; + + EXPECT_CALL(sampler, period()).Times(3).WillRepeatedly(Return(16)); + EXPECT_CALL(sampler, GetExponentialBiased(16)) + .WillOnce(Return(1)) + .WillOnce(Return(2)) + .WillOnce(Return(3)); + + EXPECT_FALSE(sampler.Sample()); + EXPECT_TRUE(sampler.Sample()); + + EXPECT_FALSE(sampler.Sample()); + EXPECT_FALSE(sampler.Sample()); + EXPECT_TRUE(sampler.Sample()); + + EXPECT_FALSE(sampler.Sample()); + EXPECT_FALSE(sampler.Sample()); + EXPECT_FALSE(sampler.Sample()); +} + +TEST(PeriodicSamplerBaseTest, ImmediatelySample) { + StrictMock sampler; + + EXPECT_CALL(sampler, period()).Times(2).WillRepeatedly(Return(16)); + EXPECT_CALL(sampler, GetExponentialBiased(16)) + .WillOnce(Return(0)) + .WillOnce(Return(1)) + .WillOnce(Return(2)); + + EXPECT_TRUE(sampler.Sample()); + + EXPECT_FALSE(sampler.Sample()); + EXPECT_TRUE(sampler.Sample()); + + EXPECT_FALSE(sampler.Sample()); + EXPECT_FALSE(sampler.Sample()); +} + +TEST(PeriodicSamplerBaseTest, Disabled) { + StrictMock sampler; + + EXPECT_CALL(sampler, period()).Times(3).WillRepeatedly(Return(0)); + + EXPECT_FALSE(sampler.Sample()); + EXPECT_FALSE(sampler.Sample()); + EXPECT_FALSE(sampler.Sample()); +} + +TEST(PeriodicSamplerBaseTest, AlwaysOn) { + StrictMock sampler; + + EXPECT_CALL(sampler, period()).Times(3).WillRepeatedly(Return(1)); + + EXPECT_TRUE(sampler.Sample()); + EXPECT_TRUE(sampler.Sample()); + EXPECT_TRUE(sampler.Sample()); +} + +TEST(PeriodicSamplerBaseTest, Disable) { + StrictMock sampler; + + EXPECT_CALL(sampler, period()).WillOnce(Return(16)); + EXPECT_CALL(sampler, GetExponentialBiased(16)).WillOnce(Return(2)); + EXPECT_FALSE(sampler.Sample()); + EXPECT_FALSE(sampler.Sample()); + + EXPECT_CALL(sampler, period()).Times(2).WillRepeatedly(Return(0)); + + EXPECT_FALSE(sampler.Sample()); + EXPECT_FALSE(sampler.Sample()); +} + +TEST(PeriodicSamplerBaseTest, Enable) { + StrictMock sampler; + + EXPECT_CALL(sampler, period()).WillOnce(Return(0)); + EXPECT_FALSE(sampler.Sample()); + + EXPECT_CALL(sampler, period()).Times(2).WillRepeatedly(Return(16)); + EXPECT_CALL(sampler, GetExponentialBiased(16)) + .Times(2) + .WillRepeatedly(Return(2)); + + EXPECT_FALSE(sampler.Sample()); + EXPECT_FALSE(sampler.Sample()); + EXPECT_TRUE(sampler.Sample()); + + EXPECT_FALSE(sampler.Sample()); + EXPECT_FALSE(sampler.Sample()); +} + +TEST(PeriodicSamplerTest, ConstructConstInit) { + struct Tag {}; + ABSL_CONST_INIT static PeriodicSampler sampler; + (void)sampler; +} + +TEST(PeriodicSamplerTest, DefaultPeriod0) { + struct Tag {}; + PeriodicSampler sampler; + EXPECT_THAT(sampler.period(), Eq(0)); +} + +TEST(PeriodicSamplerTest, DefaultPeriod) { + struct Tag {}; + PeriodicSampler sampler; + EXPECT_THAT(sampler.period(), Eq(100)); +} + +TEST(PeriodicSamplerTest, SetGlobalPeriod) { + struct Tag1 {}; + struct Tag2 {}; + PeriodicSampler sampler1; + PeriodicSampler sampler2; + + EXPECT_THAT(sampler1.period(), Eq(25)); + EXPECT_THAT(sampler2.period(), Eq(50)); + + std::thread thread([] { + PeriodicSampler sampler1; + PeriodicSampler sampler2; + EXPECT_THAT(sampler1.period(), Eq(25)); + EXPECT_THAT(sampler2.period(), Eq(50)); + sampler1.SetGlobalPeriod(10); + sampler2.SetGlobalPeriod(20); + }); + thread.join(); + + EXPECT_THAT(sampler1.period(), Eq(10)); + EXPECT_THAT(sampler2.period(), Eq(20)); +} + +} // namespace +} // namespace base_internal +} // namespace absl diff --git a/absl/debugging/internal/stacktrace_win32-inl.inc b/absl/debugging/internal/stacktrace_win32-inl.inc index b46491f8143..247e7428df8 100644 --- a/absl/debugging/internal/stacktrace_win32-inl.inc +++ b/absl/debugging/internal/stacktrace_win32-inl.inc @@ -54,7 +54,7 @@ static RtlCaptureStackBackTrace_Function* const RtlCaptureStackBackTrace_fn = template static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count, - const void *ucp, int *min_dropped_frames) { + const void*, int* min_dropped_frames) { int n = 0; if (!RtlCaptureStackBackTrace_fn) { // can't find a stacktrace with no function to call diff --git a/absl/debugging/symbolize_win32.inc b/absl/debugging/symbolize_win32.inc index b0fb3b9a3e1..bd6fbd5ef46 100644 --- a/absl/debugging/symbolize_win32.inc +++ b/absl/debugging/symbolize_win32.inc @@ -34,7 +34,7 @@ namespace absl { static HANDLE process = NULL; -void InitializeSymbolizer(const char *argv0) { +void InitializeSymbolizer(const char*) { if (process != nullptr) { return; } @@ -53,12 +53,12 @@ void InitializeSymbolizer(const char *argv0) { } } -bool Symbolize(const void *pc, char *out, int out_size) { +bool Symbolize(const void* pc, char* out, int out_size) { if (out_size <= 0) { return false; } alignas(SYMBOL_INFO) char buf[sizeof(SYMBOL_INFO) + MAX_SYM_NAME]; - SYMBOL_INFO *symbol = reinterpret_cast(buf); + SYMBOL_INFO* symbol = reinterpret_cast(buf); symbol->SizeOfStruct = sizeof(SYMBOL_INFO); symbol->MaxNameLen = MAX_SYM_NAME; if (!SymFromAddr(process, reinterpret_cast(pc), nullptr, symbol)) { diff --git a/absl/synchronization/notification.cc b/absl/synchronization/notification.cc index 53ace00870c..1eb7f416190 100644 --- a/absl/synchronization/notification.cc +++ b/absl/synchronization/notification.cc @@ -44,15 +44,6 @@ Notification::~Notification() { MutexLock l(&this->mutex_); } -static inline bool HasBeenNotifiedInternal( - const std::atomic *notified_yet) { - return notified_yet->load(std::memory_order_acquire); -} - -bool Notification::HasBeenNotified() const { - return HasBeenNotifiedInternal(&this->notified_yet_); -} - void Notification::WaitForNotification() const { if (!HasBeenNotifiedInternal(&this->notified_yet_)) { this->mutex_.LockWhen(Condition(&HasBeenNotifiedInternal, diff --git a/absl/synchronization/notification.h b/absl/synchronization/notification.h index 82d111a5492..36c2ce3dcef 100644 --- a/absl/synchronization/notification.h +++ b/absl/synchronization/notification.h @@ -73,7 +73,9 @@ class Notification { // Notification::HasBeenNotified() // // Returns the value of the notification's internal "notified" state. - bool HasBeenNotified() const; + bool HasBeenNotified() const { + return HasBeenNotifiedInternal(&this->notified_yet_); + } // Notification::WaitForNotification() // @@ -105,6 +107,11 @@ class Notification { void Notify(); private: + static inline bool HasBeenNotifiedInternal( + const std::atomic* notified_yet) { + return notified_yet->load(std::memory_order_acquire); + } + mutable Mutex mutex_; std::atomic notified_yet_; // written under mutex_ }; diff --git a/absl/time/internal/cctz/src/zone_info_source.cc b/absl/time/internal/cctz/src/zone_info_source.cc index 42f50c5e8fb..d5758efa295 100644 --- a/absl/time/internal/cctz/src/zone_info_source.cc +++ b/absl/time/internal/cctz/src/zone_info_source.cc @@ -49,10 +49,13 @@ std::unique_ptr DefaultFactory( #if !defined(__has_attribute) #define __has_attribute(x) 0 #endif -#if __has_attribute(weak) || defined(__GNUC__) +// MinGW is GCC on Windows, so while it asserts __has_attribute(weak), the +// Windows linker cannot handle that. Nor does the MinGW compiler know how to +// pass "#pragma comment(linker, ...)" to the Windows linker. +#if (__has_attribute(weak) || defined(__GNUC__)) && !defined(__MINGW32__) ZoneInfoSourceFactory zone_info_source_factory __attribute__((weak)) = DefaultFactory; -#elif defined(_MSC_VER) && !defined(_LIBCPP_VERSION) +#elif defined(_MSC_VER) && !defined(__MINGW32__) && !defined(_LIBCPP_VERSION) extern ZoneInfoSourceFactory zone_info_source_factory; extern ZoneInfoSourceFactory default_factory; ZoneInfoSourceFactory default_factory = DefaultFactory; From 2103fd9acdf58279f739860bff3f8c9f4b845605 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Mon, 18 Nov 2019 11:02:26 -0800 Subject: [PATCH 0019/3164] Export of internal Abseil changes -- d447fdcb801036cf08197eece193a5a706661120 by Gennadiy Rozental : Eliminate the need for static function holding help message. This decreases the cost of ABSL_FLAG abstraction by 120 bytes under clang. PiperOrigin-RevId: 281107806 -- 0aa6b91189f0e8b2381438c33465673a7ae02487 by Derek Mauro : Disable the weak symbol CCTZ extension in the time test_util on MinGW, which does not support it. PiperOrigin-RevId: 280719769 -- 67322c41c3e776eb541de90fa4526bdb49422eb6 by Abseil Team : Tune PeriodicSampler implementation (for internal-use only) PiperOrigin-RevId: 280708943 -- 3a48c346340c7ed03816645cd327e1ff07729aa4 by Abseil Team : Clean up public headers not to have warnings for "-Wcomma" PiperOrigin-RevId: 280695373 -- 981acd1ef3b13a83a84f04f11c8931f4ed4451c9 by Matthew Brown : Release absl::int128. PiperOrigin-RevId: 280690817 -- d30fae9d2ec30b81322d2eb5afe7e13e45b4b422 by Derek Mauro : Fix -Wundef warnings in random platform detection PiperOrigin-RevId: 280669598 GitOrigin-RevId: d447fdcb801036cf08197eece193a5a706661120 Change-Id: Ie5e10e567c54b7de211833607689f233d4ddf734 --- absl/base/internal/bits.h | 35 +- absl/base/internal/periodic_sampler.cc | 6 +- absl/base/internal/periodic_sampler.h | 62 +- absl/flags/flag.h | 57 +- absl/flags/flag_test.cc | 17 +- absl/flags/internal/commandlineflag.cc | 7 - absl/flags/internal/commandlineflag.h | 47 +- absl/flags/internal/flag.cc | 5 + absl/flags/internal/flag.h | 89 ++- absl/flags/internal/registry.cc | 6 +- absl/numeric/int128.cc | 129 +++ absl/numeric/int128.h | 363 ++++++++- absl/numeric/int128_have_intrinsic.inc | 284 +++++++ absl/numeric/int128_no_intrinsic.inc | 290 +++++++ absl/numeric/int128_stream_test.cc | 729 +++++++++++++++++ absl/numeric/int128_test.cc | 743 ++++++++++++++++++ absl/random/internal/platform.h | 3 +- absl/strings/internal/str_format/arg.cc | 12 + absl/strings/internal/str_format/arg.h | 4 + .../internal/str_format/convert_test.cc | 37 +- absl/strings/string_view.h | 5 +- absl/time/internal/test_util.cc | 3 + absl/time/time.h | 3 +- 23 files changed, 2800 insertions(+), 136 deletions(-) diff --git a/absl/base/internal/bits.h b/absl/base/internal/bits.h index b0780f2d121..450b923f4a4 100644 --- a/absl/base/internal/bits.h +++ b/absl/base/internal/bits.h @@ -50,10 +50,22 @@ namespace base_internal { ABSL_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros64Slow(uint64_t n) { int zeroes = 60; - if (n >> 32) zeroes -= 32, n >>= 32; - if (n >> 16) zeroes -= 16, n >>= 16; - if (n >> 8) zeroes -= 8, n >>= 8; - if (n >> 4) zeroes -= 4, n >>= 4; + if (n >> 32) { + zeroes -= 32; + n >>= 32; + } + if (n >> 16) { + zeroes -= 16; + n >>= 16; + } + if (n >> 8) { + zeroes -= 8; + n >>= 8; + } + if (n >> 4) { + zeroes -= 4; + n >>= 4; + } return "\4\3\2\2\1\1\1\1\0\0\0\0\0\0\0"[n] + zeroes; } @@ -95,9 +107,18 @@ ABSL_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros64(uint64_t n) { ABSL_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros32Slow(uint64_t n) { int zeroes = 28; - if (n >> 16) zeroes -= 16, n >>= 16; - if (n >> 8) zeroes -= 8, n >>= 8; - if (n >> 4) zeroes -= 4, n >>= 4; + if (n >> 16) { + zeroes -= 16; + n >>= 16; + } + if (n >> 8) { + zeroes -= 8; + n >>= 8; + } + if (n >> 4) { + zeroes -= 4; + n >>= 4; + } return "\4\3\2\2\1\1\1\1\0\0\0\0\0\0\0"[n] + zeroes; } diff --git a/absl/base/internal/periodic_sampler.cc b/absl/base/internal/periodic_sampler.cc index 439745d0c51..69656c8a0fb 100644 --- a/absl/base/internal/periodic_sampler.cc +++ b/absl/base/internal/periodic_sampler.cc @@ -36,13 +36,13 @@ bool PeriodicSamplerBase::SubtleConfirmSample() noexcept { // Check if this is the first call to Sample() if (ABSL_PREDICT_FALSE(stride_ == 1)) { - stride_ = -1 - GetExponentialBiased(current_period); - if (stride_ < -1) { + stride_ = static_cast(-1 - GetExponentialBiased(current_period)); + if (static_cast(stride_) < -1) { ++stride_; return false; } } - stride_ = -1 - GetExponentialBiased(current_period); + stride_ = static_cast(-1 - GetExponentialBiased(current_period)); return true; } diff --git a/absl/base/internal/periodic_sampler.h b/absl/base/internal/periodic_sampler.h index 2c0600f05ea..238797424fc 100644 --- a/absl/base/internal/periodic_sampler.h +++ b/absl/base/internal/periodic_sampler.h @@ -111,33 +111,49 @@ class PeriodicSamplerBase { // Returns the current period of this sampler. Thread-safe. virtual int period() const noexcept = 0; - int64_t stride_ = 0; + // Keep and decrement stride_ as an unsigned integer, but compare the value + // to zero casted as a signed int. clang and msvc do not create optimum code + // if we use signed for the combined decrement and sign comparison. + // + // Below 3 alternative options, all compiles generate the best code + // using the unsigned increment <---> signed int comparison option. + // + // Option 1: + // int64_t stride_; + // if (ABSL_PREDICT_TRUE(++stride_ < 0)) { ... } + // + // GCC x64 (OK) : https://gcc.godbolt.org/z/R5MzzA + // GCC ppc (OK) : https://gcc.godbolt.org/z/z7NZAt + // Clang x64 (BAD): https://gcc.godbolt.org/z/t4gPsd + // ICC x64 (OK) : https://gcc.godbolt.org/z/rE6s8W + // MSVC x64 (OK) : https://gcc.godbolt.org/z/ARMXqS + // + // Option 2: + // int64_t stride_ = 0; + // if (ABSL_PREDICT_TRUE(--stride_ >= 0)) { ... } + // + // GCC x64 (OK) : https://gcc.godbolt.org/z/jSQxYK + // GCC ppc (OK) : https://gcc.godbolt.org/z/VJdYaA + // Clang x64 (BAD): https://gcc.godbolt.org/z/Xm4NjX + // ICC x64 (OK) : https://gcc.godbolt.org/z/4snaFd + // MSVC x64 (BAD): https://gcc.godbolt.org/z/BgnEKE + // + // Option 3: + // uint64_t stride_; + // if (ABSL_PREDICT_TRUE(static_cast(++stride_) < 0)) { ... } + // + // GCC x64 (OK) : https://gcc.godbolt.org/z/bFbfPy + // GCC ppc (OK) : https://gcc.godbolt.org/z/S9KkUE + // Clang x64 (OK) : https://gcc.godbolt.org/z/UYzRb4 + // ICC x64 (OK) : https://gcc.godbolt.org/z/ptTNfD + // MSVC x64 (OK) : https://gcc.godbolt.org/z/76j4-5 + uint64_t stride_ = 0; ExponentialBiased rng_; }; inline bool PeriodicSamplerBase::SubtleMaybeSample() noexcept { - // We explicitly count up and not down, as the compiler does not generate - // ideal code for counting down. See also https://gcc.godbolt.org/z/FTPDSM - // - // With `if (ABSL_PREDICT_FALSE(++stride_ < 0))` - // add QWORD PTR fs:sampler@tpoff+8, 1 - // jns .L15 - // ret - // - // With `if (ABSL_PREDICT_FALSE(--stride_ > 0))` - // mov rax, QWORD PTR fs:sampler@tpoff+8 - // sub rax, 1 - // mov QWORD PTR fs:sampler@tpoff+8, rax - // test rax, rax - // jle .L15 - // ret - // add QWORD PTR fs:sampler@tpoff+8, 1 - // jns .L15 - // ret - // - // --stride >= 0 does work, but makes our logic slightly harder as in that - // case we have less convenient zero-init and over-run values. - if (ABSL_PREDICT_FALSE(++stride_ < 0)) { + // See comments on `stride_` for the unsigned increment / signed compare. + if (ABSL_PREDICT_TRUE(static_cast(++stride_) < 0)) { return false; } return true; diff --git a/absl/flags/flag.h b/absl/flags/flag.h index 356ddb61607..09af47a06d1 100644 --- a/absl/flags/flag.h +++ b/absl/flags/flag.h @@ -96,8 +96,7 @@ class Flag { constexpr Flag(const char* name, const flags_internal::HelpGenFunc help_gen, const char* filename, const flags_internal::FlagMarshallingOpFn marshalling_op, - const flags_internal::InitialValGenFunc initial_value_gen, - bool, void*) + const flags_internal::InitialValGenFunc initial_value_gen) : name_(name), help_gen_(help_gen), filename_(filename), @@ -115,8 +114,11 @@ class Flag { return impl_; } - impl_ = new flags_internal::Flag(name_, help_gen_, filename_, - marshalling_op_, initial_value_gen_); + impl_ = new flags_internal::Flag( + name_, + {flags_internal::FlagHelpSrc(help_gen_), + flags_internal::FlagHelpSrcKind::kGenFunc}, + filename_, marshalling_op_, initial_value_gen_); inited_.store(true, std::memory_order_release); } @@ -307,9 +309,19 @@ void SetFlag(absl::Flag* flag, const V& v) { #define ABSL_FLAG_IMPL_FLAGHELP(txt) txt #endif -#define ABSL_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, txt) \ - static std::string AbslFlagsWrapHelp##name() { \ - return ABSL_FLAG_IMPL_FLAGHELP(txt); \ +// AbslFlagHelpGenFor##name is used to encapsulate both immediate (method Const) +// and lazy (method NonConst) evaluation of help message expression. We choose +// between the two via the call to HelpArg in absl::Flag instantiation below. +// If help message expression is constexpr evaluable compiler will optimize +// away this whole struct. +#define ABSL_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, txt) \ + struct AbslFlagHelpGenFor##name { \ + template \ + static constexpr const char* Const() { \ + return absl::flags_internal::HelpConstexprWrap( \ + ABSL_FLAG_IMPL_FLAGHELP(txt)); \ + } \ + static std::string NonConst() { return ABSL_FLAG_IMPL_FLAGHELP(txt); } \ } #define ABSL_FLAG_IMPL_DECLARE_DEF_VAL_WRAPPER(name, Type, default_value) \ @@ -326,29 +338,28 @@ void SetFlag(absl::Flag* flag, const V& v) { #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_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, help); \ ABSL_CONST_INIT absl::Flag FLAGS_##name{ \ - ABSL_FLAG_IMPL_FLAGNAME(#name), &AbslFlagsWrapHelp##name, \ + ABSL_FLAG_IMPL_FLAGNAME(#name), \ + absl::flags_internal::HelpArg(0), \ ABSL_FLAG_IMPL_FILENAME(), \ &absl::flags_internal::FlagMarshallingOps, \ &AbslFlagsInitFlag##name}; \ extern bool FLAGS_no##name; \ bool FLAGS_no##name = ABSL_FLAG_IMPL_REGISTRAR(Type, FLAGS_##name) #else -// MSVC version uses aggregate initialization. -#define ABSL_FLAG_IMPL(Type, name, default_value, help) \ - namespace absl /* block flags in namespaces */ {} \ - ABSL_FLAG_IMPL_DECLARE_DEF_VAL_WRAPPER(name, Type, default_value) \ - ABSL_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, help) \ - ABSL_CONST_INIT absl::Flag FLAGS_##name{ \ - ABSL_FLAG_IMPL_FLAGNAME(#name), \ - &AbslFlagsWrapHelp##name, \ - ABSL_FLAG_IMPL_FILENAME(), \ - &absl::flags_internal::FlagMarshallingOps, \ - &AbslFlagsInitFlag##name, \ - false, \ - nullptr}; \ - extern bool FLAGS_no##name; \ +// MSVC version uses aggregate initialization. We also do not try to +// optimize away help wrapper. +#define ABSL_FLAG_IMPL(Type, name, default_value, help) \ + namespace absl /* block flags in namespaces */ {} \ + ABSL_FLAG_IMPL_DECLARE_DEF_VAL_WRAPPER(name, Type, default_value) \ + ABSL_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, help); \ + ABSL_CONST_INIT absl::Flag FLAGS_##name{ \ + ABSL_FLAG_IMPL_FLAGNAME(#name), &AbslFlagHelpGenFor##name::NonConst, \ + ABSL_FLAG_IMPL_FILENAME(), \ + &absl::flags_internal::FlagMarshallingOps, \ + &AbslFlagsInitFlag##name}; \ + extern bool FLAGS_no##name; \ bool FLAGS_no##name = ABSL_FLAG_IMPL_REGISTRAR(Type, FLAGS_##name) #endif diff --git a/absl/flags/flag_test.cc b/absl/flags/flag_test.cc index 59dc579ce02..4e08da8589a 100644 --- a/absl/flags/flag_test.cc +++ b/absl/flags/flag_test.cc @@ -32,7 +32,7 @@ namespace { namespace flags = absl::flags_internal; -std::string TestHelpMsg() { return "help"; } +std::string TestHelpMsg() { return "dynamic help"; } template void* TestMakeDflt() { return new T{}; @@ -41,19 +41,22 @@ void TestCallback() {} template bool TestConstructionFor() { - constexpr flags::Flag f1("f1", &TestHelpMsg, "file", + constexpr flags::HelpInitArg help_arg{flags::FlagHelpSrc("literal help"), + flags::FlagHelpSrcKind::kLiteral}; + constexpr flags::Flag f1("f1", help_arg, "file", &flags::FlagMarshallingOps, &TestMakeDflt); EXPECT_EQ(f1.Name(), "f1"); - EXPECT_EQ(f1.Help(), "help"); + EXPECT_EQ(f1.Help(), "literal help"); EXPECT_EQ(f1.Filename(), "file"); - ABSL_CONST_INIT static flags::Flag f2("f2", &TestHelpMsg, "file", - &flags::FlagMarshallingOps, - &TestMakeDflt); + ABSL_CONST_INIT static flags::Flag f2( + "f2", + {flags::FlagHelpSrc(&TestHelpMsg), flags::FlagHelpSrcKind::kGenFunc}, + "file", &flags::FlagMarshallingOps, &TestMakeDflt); flags::FlagRegistrar(&f2).OnUpdate(TestCallback); EXPECT_EQ(f2.Name(), "f2"); - EXPECT_EQ(f2.Help(), "help"); + EXPECT_EQ(f2.Help(), "dynamic help"); EXPECT_EQ(f2.Filename(), "file"); return true; diff --git a/absl/flags/internal/commandlineflag.cc b/absl/flags/internal/commandlineflag.cc index caf33bc4892..88f91e16d19 100644 --- a/absl/flags/internal/commandlineflag.cc +++ b/absl/flags/internal/commandlineflag.cc @@ -56,12 +56,5 @@ std::string CommandLineFlag::Filename() const { return flags_internal::GetUsageConfig().normalize_filename(filename_); } -std::string HelpText::GetHelpText() const { - if (help_function_) return help_function_(); - if (help_message_) return help_message_; - - return {}; -} - } // namespace flags_internal } // namespace absl diff --git a/absl/flags/internal/commandlineflag.h b/absl/flags/internal/commandlineflag.h index 13a3352e80f..7964f1bf8c4 100644 --- a/absl/flags/internal/commandlineflag.h +++ b/absl/flags/internal/commandlineflag.h @@ -60,14 +60,6 @@ enum ValueSource { kProgrammaticChange, }; -// Signature for the help generation function used as an argument for the -// absl::Flag constructor. -using HelpGenFunc = std::string (*)(); - -// Signature for the function generating the initial flag value based (usually -// based on default value supplied in flag's definition) -using InitialValGenFunc = void* (*)(); - extern const char kStrippedFlagHelp[]; // The per-type function @@ -149,34 +141,6 @@ inline size_t Sizeof(FlagOpFn op) { op(flags_internal::kSizeof, nullptr, nullptr))); } -// Holds either a pointer to help text or a function which produces it. This is -// needed for supporting both static initialization of Flags while supporting -// the legacy registration framework. We can't use absl::variant since anybody passing 0 or nullptr in to a CommandLineFlag -// would find an ambiguity. -class HelpText { - public: - static constexpr HelpText FromFunctionPointer(const HelpGenFunc fn) { - return HelpText(fn, nullptr); - } - static constexpr HelpText FromStaticCString(const char* msg) { - return HelpText(nullptr, msg); - } - - std::string GetHelpText() const; - - HelpText() = delete; - HelpText(const HelpText&) = default; - HelpText(HelpText&&) = default; - - private: - explicit constexpr HelpText(const HelpGenFunc fn, const char* msg) - : help_function_(fn), help_message_(msg) {} - - HelpGenFunc help_function_; - const char* help_message_; -}; - // Handle to FlagState objects. Specific flag state objects will restore state // of a flag produced this flag state from method CommandLineFlag::SaveState(). class FlagStateInterface { @@ -190,9 +154,8 @@ class FlagStateInterface { // Holds all information for a flag. class CommandLineFlag { public: - constexpr CommandLineFlag(const char* name, HelpText help_text, - const char* filename) - : name_(name), help_(help_text), filename_(filename) {} + constexpr CommandLineFlag(const char* name, const char* filename) + : name_(name), filename_(filename) {} // Virtual destructor virtual void Destroy() const = 0; @@ -203,7 +166,6 @@ class CommandLineFlag { // Non-polymorphic access methods. absl::string_view Name() const { return name_; } - std::string Help() const { return help_.GetHelpText(); } absl::string_view Typename() const; std::string Filename() const; @@ -250,6 +212,8 @@ class CommandLineFlag { // Polymorphic access methods + // Returns help message associated with this flag + virtual std::string Help() const = 0; // Returns true iff this object corresponds to retired flag virtual bool IsRetired() const { return false; } // Returns true iff this is a handle to an Abseil Flag. @@ -285,12 +249,11 @@ class CommandLineFlag { // flag's value type. virtual void CheckDefaultValueParsingRoundtrip() const = 0; - // Constant configuration for a particular flag. protected: ~CommandLineFlag() = default; + // Constant configuration for a particular flag. const char* const name_; // Flags name passed to ABSL_FLAG as second arg. - const HelpText help_; // The function generating help message. const char* const filename_; // The file name where ABSL_FLAG resides. private: diff --git a/absl/flags/internal/flag.cc b/absl/flags/internal/flag.cc index 061113d7655..d087f79e891 100644 --- a/absl/flags/internal/flag.cc +++ b/absl/flags/internal/flag.cc @@ -85,6 +85,11 @@ void FlagImpl::Destroy() const { delete locks_; } +std::string FlagImpl::Help() const { + return help_source_kind_ == FlagHelpSrcKind::kLiteral ? help_.literal + : help_.gen_func(); +} + bool FlagImpl::IsModified() const { absl::MutexLock l(DataGuard()); return modified_; diff --git a/absl/flags/internal/flag.h b/absl/flags/internal/flag.h index ce0ccf2d31d..d79902543a2 100644 --- a/absl/flags/internal/flag.h +++ b/absl/flags/internal/flag.h @@ -61,6 +61,68 @@ class FlagState : public flags_internal::FlagStateInterface { int64_t counter_; }; +// This is help argument for absl::Flag encapsulating the string literal pointer +// or pointer to function generating it as well as enum descriminating two +// cases. +using HelpGenFunc = std::string (*)(); + +union FlagHelpSrc { + constexpr explicit FlagHelpSrc(const char* help_msg) : literal(help_msg) {} + constexpr explicit FlagHelpSrc(HelpGenFunc help_gen) : gen_func(help_gen) {} + + const char* literal; + HelpGenFunc gen_func; +}; + +enum class FlagHelpSrcKind : int8_t { kLiteral, kGenFunc }; + +struct HelpInitArg { + FlagHelpSrc source; + FlagHelpSrcKind kind; +}; + +// HelpConstexprWrap is used by struct AbslFlagHelpGenFor##name generated by +// ABSL_FLAG macro. It is only used to silence the compiler in the case where +// help message expression is not constexpr and does not have type const char*. +// If help message expression is indeed constexpr const char* HelpConstexprWrap +// is just a trivial identity function. +template +const char* HelpConstexprWrap(const T&) { + return nullptr; +} +constexpr const char* HelpConstexprWrap(const char* p) { return p; } +constexpr const char* HelpConstexprWrap(char* p) { return p; } + +// These two HelpArg overloads allows us to select at compile time one of two +// way to pass Help argument to absl::Flag. We'll be passing +// AbslFlagHelpGenFor##name as T and integer 0 as a single argument to prefer +// first overload if possible. If T::Const is evaluatable on constexpr +// context (see non template int parameter below) we'll choose first overload. +// In this case the help message expression is immediately evaluated and is used +// to construct the absl::Flag. No additionl code is generated by ABSL_FLAG. +// Otherwise SFINAE kicks in and first overload is dropped from the +// consideration, in which case the second overload will be used. The second +// overload does not attempt to evaluate the help message expression +// immediately and instead delays the evaluation by returing the function +// pointer (&T::NonConst) genering the help message when necessary. This is +// evaluatable in constexpr context, but the cost is an extra function being +// generated in the ABSL_FLAG code. +template +constexpr flags_internal::HelpInitArg HelpArg(int) { + return {flags_internal::FlagHelpSrc(T::Const()), + flags_internal::FlagHelpSrcKind::kLiteral}; +} + +template +constexpr flags_internal::HelpInitArg HelpArg(char) { + return {flags_internal::FlagHelpSrc(&T::NonConst), + flags_internal::FlagHelpSrcKind::kGenFunc}; +} + +// Signature for the function generating the initial flag value based (usually +// based on default value supplied in flag's definition) +using InitialValGenFunc = void* (*)(); + // Signature for the mutation callback used by watched Flags // The callback is noexcept. // TODO(rogeeff): add noexcept after C++17 support is added. @@ -74,15 +136,19 @@ class FlagImpl { public: constexpr FlagImpl(const flags_internal::FlagOpFn op, const flags_internal::FlagMarshallingOpFn marshalling_op, - const flags_internal::InitialValGenFunc initial_value_gen) + const flags_internal::InitialValGenFunc initial_value_gen, + const HelpInitArg help) : op_(op), marshalling_op_(marshalling_op), - initial_value_gen_(initial_value_gen) {} + initial_value_gen_(initial_value_gen), + help_(help.source), + help_source_kind_(help.kind) {} // Forces destruction of the Flag's data. void Destroy() const; // Constant access methods + std::string Help() const; bool IsModified() const ABSL_LOCKS_EXCLUDED(locks_->primary_mu); bool IsSpecifiedOnCommandLine() const ABSL_LOCKS_EXCLUDED(locks_->primary_mu); std::string DefaultValue() const ABSL_LOCKS_EXCLUDED(locks_->primary_mu); @@ -149,9 +215,12 @@ class FlagImpl { absl::Mutex* DataGuard() const ABSL_LOCK_RETURNED(locks_->primary_mu); // Immutable Flag's data. - const FlagOpFn op_; // Type-specific handler - const FlagMarshallingOpFn marshalling_op_; // Marshalling ops handler - const InitialValGenFunc initial_value_gen_; // Makes flag's initial value + const FlagOpFn op_; // Type-specific handler. + const FlagMarshallingOpFn marshalling_op_; // Marshalling ops handler. + const InitialValGenFunc initial_value_gen_; // Makes flag's initial value. + const FlagHelpSrc help_; // Help message literal or function to generate it. + // Indicates if help message was supplied as literal or generator func. + const FlagHelpSrcKind help_source_kind_; // Mutable Flag's data. (guarded by locks_->primary_mu). // Indicates that locks_, cur_ and def_ fields have been lazily initialized. @@ -191,14 +260,13 @@ class FlagImpl { template class Flag final : public flags_internal::CommandLineFlag { public: - constexpr Flag(const char* name, const flags_internal::HelpGenFunc help_gen, + constexpr Flag(const char* name, const flags_internal::HelpInitArg help, const char* filename, const flags_internal::FlagMarshallingOpFn marshalling_op, const flags_internal::InitialValGenFunc initial_value_gen) - : flags_internal::CommandLineFlag( - name, flags_internal::HelpText::FromFunctionPointer(help_gen), - filename), - impl_(&flags_internal::FlagOps, marshalling_op, initial_value_gen) {} + : flags_internal::CommandLineFlag(name, filename), + impl_(&flags_internal::FlagOps, marshalling_op, initial_value_gen, + help) {} T Get() const { // See implementation notes in CommandLineFlag::Get(). @@ -222,6 +290,7 @@ class Flag final : public flags_internal::CommandLineFlag { } // CommandLineFlag interface + std::string Help() const override { return impl_.Help(); } bool IsModified() const override { return impl_.IsModified(); } bool IsSpecifiedOnCommandLine() const override { return impl_.IsSpecifiedOnCommandLine(); diff --git a/absl/flags/internal/registry.cc b/absl/flags/internal/registry.cc index ae7671a967e..52d9f3c716f 100644 --- a/absl/flags/internal/registry.cc +++ b/absl/flags/internal/registry.cc @@ -276,9 +276,8 @@ namespace { class RetiredFlagObj final : public flags_internal::CommandLineFlag { public: constexpr RetiredFlagObj(const char* name, FlagOpFn ops) - : flags_internal::CommandLineFlag( - name, flags_internal::HelpText::FromStaticCString(nullptr), - /*filename=*/"RETIRED"), + : flags_internal::CommandLineFlag(name, + /*filename=*/"RETIRED"), op_(ops) {} private: @@ -288,6 +287,7 @@ class RetiredFlagObj final : public flags_internal::CommandLineFlag { } flags_internal::FlagOpFn TypeId() const override { return op_; } + std::string Help() const override { return ""; } bool IsRetired() const override { return true; } bool IsModified() const override { return false; } bool IsSpecifiedOnCommandLine() const override { return false; } diff --git a/absl/numeric/int128.cc b/absl/numeric/int128.cc index 93b62c52901..1eba09de38c 100644 --- a/absl/numeric/int128.cc +++ b/absl/numeric/int128.cc @@ -244,6 +244,111 @@ std::ostream& operator<<(std::ostream& os, uint128 v) { return os << rep; } +namespace { + +uint128 UnsignedAbsoluteValue(int128 v) { + // Cast to uint128 before possibly negating because -Int128Min() is undefined. + return Int128High64(v) < 0 ? -uint128(v) : uint128(v); +} + +} // namespace + +#if !defined(ABSL_HAVE_INTRINSIC_INT128) +namespace { + +template +int128 MakeInt128FromFloat(T v) { + // Conversion when v is NaN or cannot fit into int128 would be undefined + // behavior if using an intrinsic 128-bit integer. + assert(std::isfinite(v) && (std::numeric_limits::max_exponent <= 127 || + (v >= -std::ldexp(static_cast(1), 127) && + v < std::ldexp(static_cast(1), 127)))); + + // We must convert the absolute value and then negate as needed, because + // floating point types are typically sign-magnitude. Otherwise, the + // difference between the high and low 64 bits when interpreted as two's + // complement overwhelms the precision of the mantissa. + uint128 result = v < 0 ? -MakeUint128FromFloat(-v) : MakeUint128FromFloat(v); + return MakeInt128(int128_internal::BitCastToSigned(Uint128High64(result)), + Uint128Low64(result)); +} + +} // namespace + +int128::int128(float v) : int128(MakeInt128FromFloat(v)) {} +int128::int128(double v) : int128(MakeInt128FromFloat(v)) {} +int128::int128(long double v) : int128(MakeInt128FromFloat(v)) {} + +int128 operator/(int128 lhs, int128 rhs) { + assert(lhs != Int128Min() || rhs != -1); // UB on two's complement. + + uint128 quotient = 0; + uint128 remainder = 0; + DivModImpl(UnsignedAbsoluteValue(lhs), UnsignedAbsoluteValue(rhs), + "ient, &remainder); + if ((Int128High64(lhs) < 0) != (Int128High64(rhs) < 0)) quotient = -quotient; + return MakeInt128(int128_internal::BitCastToSigned(Uint128High64(quotient)), + Uint128Low64(quotient)); +} + +int128 operator%(int128 lhs, int128 rhs) { + assert(lhs != Int128Min() || rhs != -1); // UB on two's complement. + + uint128 quotient = 0; + uint128 remainder = 0; + DivModImpl(UnsignedAbsoluteValue(lhs), UnsignedAbsoluteValue(rhs), + "ient, &remainder); + if (Int128High64(lhs) < 0) remainder = -remainder; + return MakeInt128(int128_internal::BitCastToSigned(Uint128High64(remainder)), + Uint128Low64(remainder)); +} +#endif // ABSL_HAVE_INTRINSIC_INT128 + +std::ostream& operator<<(std::ostream& os, int128 v) { + std::ios_base::fmtflags flags = os.flags(); + std::string rep; + + // Add the sign if needed. + bool print_as_decimal = + (flags & std::ios::basefield) == std::ios::dec || + (flags & std::ios::basefield) == std::ios_base::fmtflags(); + if (print_as_decimal) { + if (Int128High64(v) < 0) { + rep = "-"; + } else if (flags & std::ios::showpos) { + rep = "+"; + } + } + + rep.append(Uint128ToFormattedString( + print_as_decimal ? UnsignedAbsoluteValue(v) : uint128(v), os.flags())); + + // Add the requisite padding. + std::streamsize width = os.width(0); + if (static_cast(width) > rep.size()) { + switch (flags & std::ios::adjustfield) { + case std::ios::left: + rep.append(width - rep.size(), os.fill()); + break; + case std::ios::internal: + if (print_as_decimal && (rep[0] == '+' || rep[0] == '-')) { + rep.insert(1, width - rep.size(), os.fill()); + } else if ((flags & std::ios::basefield) == std::ios::hex && + (flags & std::ios::showbase) && v != 0) { + rep.insert(2, width - rep.size(), os.fill()); + } else { + rep.insert(0, width - rep.size(), os.fill()); + } + break; + default: // std::ios::right + rep.insert(0, width - rep.size(), os.fill()); + break; + } + } + + return os << rep; +} + } // namespace absl namespace std { @@ -270,4 +375,28 @@ constexpr int numeric_limits::max_exponent; constexpr int numeric_limits::max_exponent10; constexpr bool numeric_limits::traps; constexpr bool numeric_limits::tinyness_before; + +constexpr bool numeric_limits::is_specialized; +constexpr bool numeric_limits::is_signed; +constexpr bool numeric_limits::is_integer; +constexpr bool numeric_limits::is_exact; +constexpr bool numeric_limits::has_infinity; +constexpr bool numeric_limits::has_quiet_NaN; +constexpr bool numeric_limits::has_signaling_NaN; +constexpr float_denorm_style numeric_limits::has_denorm; +constexpr bool numeric_limits::has_denorm_loss; +constexpr float_round_style numeric_limits::round_style; +constexpr bool numeric_limits::is_iec559; +constexpr bool numeric_limits::is_bounded; +constexpr bool numeric_limits::is_modulo; +constexpr int numeric_limits::digits; +constexpr int numeric_limits::digits10; +constexpr int numeric_limits::max_digits10; +constexpr int numeric_limits::radix; +constexpr int numeric_limits::min_exponent; +constexpr int numeric_limits::min_exponent10; +constexpr int numeric_limits::max_exponent; +constexpr int numeric_limits::max_exponent10; +constexpr bool numeric_limits::traps; +constexpr bool numeric_limits::tinyness_before; } // namespace std diff --git a/absl/numeric/int128.h b/absl/numeric/int128.h index 4f965686c39..506176127fe 100644 --- a/absl/numeric/int128.h +++ b/absl/numeric/int128.h @@ -17,10 +17,7 @@ // File: int128.h // ----------------------------------------------------------------------------- // -// This header file defines 128-bit integer types. -// -// Currently, this file defines `uint128`, an unsigned 128-bit integer; -// a signed 128-bit integer is forthcoming. +// This header file defines 128-bit integer types, `uint128` and `int128`. #ifndef ABSL_NUMERIC_INT128_H_ #define ABSL_NUMERIC_INT128_H_ @@ -53,6 +50,8 @@ namespace absl { +class int128; + // uint128 // // An unsigned 128-bit integer type. The API is meant to mimic an intrinsic type @@ -116,6 +115,7 @@ class constexpr uint128(__int128 v); // NOLINT(runtime/explicit) constexpr uint128(unsigned __int128 v); // NOLINT(runtime/explicit) #endif // ABSL_HAVE_INTRINSIC_INT128 + constexpr uint128(int128 v); // NOLINT(runtime/explicit) explicit uint128(float v); explicit uint128(double v); explicit uint128(long double v); @@ -131,6 +131,7 @@ class uint128& operator=(__int128 v); uint128& operator=(unsigned __int128 v); #endif // ABSL_HAVE_INTRINSIC_INT128 + uint128& operator=(int128 v); // Conversion operators to other arithmetic types constexpr explicit operator bool() const; @@ -291,7 +292,238 @@ class numeric_limits { }; } // namespace std -// TODO(absl-team): Implement signed 128-bit type +namespace absl { + +// int128 +// +// A signed 128-bit integer type. The API is meant to mimic an intrinsic +// integral type as closely as is practical, including exhibiting undefined +// behavior in analogous cases (e.g. division by zero). +// +// An `int128` supports the following: +// +// * Implicit construction from integral types +// * Explicit conversion to integral types +// +// However, an `int128` differs from intrinsic integral types in the following +// ways: +// +// * It is not implicitly convertible to other integral types. +// * Requires explicit construction from and conversion to floating point +// types. + +// Additionally, if your compiler supports `__int128`, `int128` is +// interoperable with that type. (Abseil checks for this compatibility through +// the `ABSL_HAVE_INTRINSIC_INT128` macro.) +// +// The design goal for `int128` is that it will be compatible with a future +// `int128_t`, if that type becomes a part of the standard. +// +// Example: +// +// float y = absl::int128(17); // Error. int128 cannot be implicitly +// // converted to float. +// +// absl::int128 v; +// int64_t i = v; // Error +// int64_t i = static_cast(v); // OK +// +class int128 { + public: + int128() = default; + + // Constructors from arithmetic types + constexpr int128(int v); // NOLINT(runtime/explicit) + constexpr int128(unsigned int v); // NOLINT(runtime/explicit) + constexpr int128(long v); // NOLINT(runtime/int) + constexpr int128(unsigned long v); // NOLINT(runtime/int) + constexpr int128(long long v); // NOLINT(runtime/int) + constexpr int128(unsigned long long v); // NOLINT(runtime/int) +#ifdef ABSL_HAVE_INTRINSIC_INT128 + constexpr int128(__int128 v); // NOLINT(runtime/explicit) + constexpr explicit int128(unsigned __int128 v); +#endif // ABSL_HAVE_INTRINSIC_INT128 + constexpr explicit int128(uint128 v); + explicit int128(float v); + explicit int128(double v); + explicit int128(long double v); + + // Assignment operators from arithmetic types + int128& operator=(int v); + int128& operator=(unsigned int v); + int128& operator=(long v); // NOLINT(runtime/int) + int128& operator=(unsigned long v); // NOLINT(runtime/int) + int128& operator=(long long v); // NOLINT(runtime/int) + int128& operator=(unsigned long long v); // NOLINT(runtime/int) +#ifdef ABSL_HAVE_INTRINSIC_INT128 + int128& operator=(__int128 v); +#endif // ABSL_HAVE_INTRINSIC_INT128 + + // Conversion operators to other arithmetic types + constexpr explicit operator bool() const; + constexpr explicit operator char() const; + constexpr explicit operator signed char() const; + constexpr explicit operator unsigned char() const; + constexpr explicit operator char16_t() const; + constexpr explicit operator char32_t() const; + constexpr explicit operator ABSL_INTERNAL_WCHAR_T() const; + constexpr explicit operator short() const; // NOLINT(runtime/int) + // NOLINTNEXTLINE(runtime/int) + constexpr explicit operator unsigned short() const; + constexpr explicit operator int() const; + constexpr explicit operator unsigned int() const; + constexpr explicit operator long() const; // NOLINT(runtime/int) + // NOLINTNEXTLINE(runtime/int) + constexpr explicit operator unsigned long() const; + // NOLINTNEXTLINE(runtime/int) + constexpr explicit operator long long() const; + // NOLINTNEXTLINE(runtime/int) + constexpr explicit operator unsigned long long() const; +#ifdef ABSL_HAVE_INTRINSIC_INT128 + constexpr explicit operator __int128() const; + constexpr explicit operator unsigned __int128() const; +#endif // ABSL_HAVE_INTRINSIC_INT128 + explicit operator float() const; + explicit operator double() const; + explicit operator long double() const; + + // Trivial copy constructor, assignment operator and destructor. + + // Arithmetic operators + int128& operator+=(int128 other); + int128& operator-=(int128 other); + int128& operator*=(int128 other); + int128& operator/=(int128 other); + int128& operator%=(int128 other); + int128 operator++(int); // postfix increment: i++ + int128 operator--(int); // postfix decrement: i-- + int128& operator++(); // prefix increment: ++i + int128& operator--(); // prefix decrement: --i + int128& operator&=(int128 other); + int128& operator|=(int128 other); + int128& operator^=(int128 other); + int128& operator<<=(int amount); + int128& operator>>=(int amount); + + // Int128Low64() + // + // Returns the lower 64-bit value of a `int128` value. + friend constexpr uint64_t Int128Low64(int128 v); + + // Int128High64() + // + // Returns the higher 64-bit value of a `int128` value. + friend constexpr int64_t Int128High64(int128 v); + + // MakeInt128() + // + // Constructs a `int128` numeric value from two 64-bit integers. Note that + // signedness is conveyed in the upper `high` value. + // + // (absl::int128(1) << 64) * high + low + // + // Note that this factory function is the only way to construct a `int128` + // from integer values greater than 2^64 or less than -2^64. + // + // Example: + // + // absl::int128 big = absl::MakeInt128(1, 0); + // absl::int128 big_n = absl::MakeInt128(-1, 0); + friend constexpr int128 MakeInt128(int64_t high, uint64_t low); + + // Int128Max() + // + // Returns the maximum value for a 128-bit signed integer. + friend constexpr int128 Int128Max(); + + // Int128Min() + // + // Returns the minimum value for a 128-bit signed integer. + friend constexpr int128 Int128Min(); + + // Support for absl::Hash. + template + friend H AbslHashValue(H h, int128 v) { + return H::combine(std::move(h), Int128High64(v), Int128Low64(v)); + } + + private: + constexpr int128(int64_t high, uint64_t low); + +#if defined(ABSL_HAVE_INTRINSIC_INT128) + __int128 v_; +#else // ABSL_HAVE_INTRINSIC_INT128 +#if defined(ABSL_IS_LITTLE_ENDIAN) + uint64_t lo_; + int64_t hi_; +#elif defined(ABSL_IS_BIG_ENDIAN) + int64_t hi_; + uint64_t lo_; +#else // byte order +#error "Unsupported byte order: must be little-endian or big-endian." +#endif // byte order +#endif // ABSL_HAVE_INTRINSIC_INT128 +}; + +std::ostream& operator<<(std::ostream& os, int128 v); + +// TODO(absl-team) add operator>>(std::istream&, int128) + +constexpr int128 Int128Max() { + return int128((std::numeric_limits::max)(), + (std::numeric_limits::max)()); +} + +constexpr int128 Int128Min() { + return int128((std::numeric_limits::min)(), 0); +} + +} // namespace absl + +// Specialized numeric_limits for int128. +namespace std { +template <> +class numeric_limits { + public: + static constexpr bool is_specialized = true; + static constexpr bool is_signed = true; + static constexpr bool is_integer = true; + static constexpr bool is_exact = true; + static constexpr bool has_infinity = false; + static constexpr bool has_quiet_NaN = false; + static constexpr bool has_signaling_NaN = false; + static constexpr float_denorm_style has_denorm = denorm_absent; + static constexpr bool has_denorm_loss = false; + static constexpr float_round_style round_style = round_toward_zero; + static constexpr bool is_iec559 = false; + static constexpr bool is_bounded = true; + static constexpr bool is_modulo = false; + static constexpr int digits = 127; + static constexpr int digits10 = 38; + static constexpr int max_digits10 = 0; + static constexpr int radix = 2; + static constexpr int min_exponent = 0; + static constexpr int min_exponent10 = 0; + static constexpr int max_exponent = 0; + static constexpr int max_exponent10 = 0; +#ifdef ABSL_HAVE_INTRINSIC_INT128 + static constexpr bool traps = numeric_limits<__int128>::traps; +#else // ABSL_HAVE_INTRINSIC_INT128 + static constexpr bool traps = numeric_limits::traps; +#endif // ABSL_HAVE_INTRINSIC_INT128 + static constexpr bool tinyness_before = false; + + static constexpr absl::int128 (min)() { return absl::Int128Min(); } + static constexpr absl::int128 lowest() { return absl::Int128Min(); } + static constexpr absl::int128 (max)() { return absl::Int128Max(); } + static constexpr absl::int128 epsilon() { return 0; } + static constexpr absl::int128 round_error() { return 0; } + static constexpr absl::int128 infinity() { return 0; } + static constexpr absl::int128 quiet_NaN() { return 0; } + static constexpr absl::int128 signaling_NaN() { return 0; } + static constexpr absl::int128 denorm_min() { return 0; } +}; +} // namespace std // -------------------------------------------------------------------------- // Implementation details follow @@ -339,6 +571,10 @@ inline uint128& uint128::operator=(unsigned __int128 v) { } #endif // ABSL_HAVE_INTRINSIC_INT128 +inline uint128& uint128::operator=(int128 v) { + return *this = uint128(v); +} + // Arithmetic operators. uint128 operator<<(uint128 lhs, int amount); @@ -420,6 +656,9 @@ constexpr uint128::uint128(unsigned __int128 v) hi_{static_cast(v >> 64)} {} #endif // ABSL_HAVE_INTRINSIC_INT128 +constexpr uint128::uint128(int128 v) + : lo_{Int128Low64(v)}, hi_{static_cast(Int128High64(v))} {} + #elif defined(ABSL_IS_BIG_ENDIAN) constexpr uint128::uint128(uint64_t high, uint64_t low) @@ -450,6 +689,9 @@ constexpr uint128::uint128(unsigned __int128 v) lo_{static_cast(v & ~uint64_t{0})} {} #endif // ABSL_HAVE_INTRINSIC_INT128 +constexpr uint128::uint128(int128 v) + : hi_{static_cast(Int128High64(v))}, lo_{Int128Low64(v)} {} + #else // byte order #error "Unsupported byte order: must be little-endian or big-endian." #endif // byte order @@ -719,6 +961,117 @@ inline uint128& uint128::operator--() { return *this; } +constexpr int128 MakeInt128(int64_t high, uint64_t low) { + return int128(high, low); +} + +// Assignment from integer types. +inline int128& int128::operator=(int v) { + return *this = int128(v); +} + +inline int128& int128::operator=(unsigned int v) { + return *this = int128(v); +} + +inline int128& int128::operator=(long v) { // NOLINT(runtime/int) + return *this = int128(v); +} + +// NOLINTNEXTLINE(runtime/int) +inline int128& int128::operator=(unsigned long v) { + return *this = int128(v); +} + +// NOLINTNEXTLINE(runtime/int) +inline int128& int128::operator=(long long v) { + return *this = int128(v); +} + +// NOLINTNEXTLINE(runtime/int) +inline int128& int128::operator=(unsigned long long v) { + return *this = int128(v); +} + +// Arithmetic operators. + +int128 operator+(int128 lhs, int128 rhs); +int128 operator-(int128 lhs, int128 rhs); +int128 operator*(int128 lhs, int128 rhs); +int128 operator/(int128 lhs, int128 rhs); +int128 operator%(int128 lhs, int128 rhs); +int128 operator|(int128 lhs, int128 rhs); +int128 operator&(int128 lhs, int128 rhs); +int128 operator^(int128 lhs, int128 rhs); +int128 operator<<(int128 lhs, int amount); +int128 operator>>(int128 lhs, int amount); + +inline int128& int128::operator+=(int128 other) { + *this = *this + other; + return *this; +} + +inline int128& int128::operator-=(int128 other) { + *this = *this - other; + return *this; +} + +inline int128& int128::operator*=(int128 other) { + *this = *this * other; + return *this; +} + +inline int128& int128::operator/=(int128 other) { + *this = *this / other; + return *this; +} + +inline int128& int128::operator%=(int128 other) { + *this = *this % other; + return *this; +} + +inline int128& int128::operator|=(int128 other) { + *this = *this | other; + return *this; +} + +inline int128& int128::operator&=(int128 other) { + *this = *this & other; + return *this; +} + +inline int128& int128::operator^=(int128 other) { + *this = *this ^ other; + return *this; +} + +inline int128& int128::operator<<=(int amount) { + *this = *this << amount; + return *this; +} + +inline int128& int128::operator>>=(int amount) { + *this = *this >> amount; + return *this; +} + +namespace int128_internal { + +// Casts from unsigned to signed while preserving the underlying binary +// representation. +constexpr int64_t BitCastToSigned(uint64_t v) { + // Casting an unsigned integer to a signed integer of the same + // width is implementation defined behavior if the source value would not fit + // in the destination type. We step around it with a roundtrip bitwise not + // operation to make sure this function remains constexpr. Clang, GCC, and + // MSVC optimize this to a no-op on x86-64. + return v & (uint64_t{1} << 63) ? ~static_cast(~v) + : static_cast(v); +} + +} // namespace int128_internal + #if defined(ABSL_HAVE_INTRINSIC_INT128) #include "absl/numeric/int128_have_intrinsic.inc" // IWYU pragma: export #else // ABSL_HAVE_INTRINSIC_INT128 diff --git a/absl/numeric/int128_have_intrinsic.inc b/absl/numeric/int128_have_intrinsic.inc index c7ea6834530..d6c76dd320c 100644 --- a/absl/numeric/int128_have_intrinsic.inc +++ b/absl/numeric/int128_have_intrinsic.inc @@ -16,3 +16,287 @@ // This file contains :int128 implementation details that depend on internal // representation when ABSL_HAVE_INTRINSIC_INT128 is defined. This file is // included by int128.h and relies on ABSL_INTERNAL_WCHAR_T being defined. + +namespace int128_internal { + +// Casts from unsigned to signed while preserving the underlying binary +// representation. +constexpr __int128 BitCastToSigned(unsigned __int128 v) { + // Casting an unsigned integer to a signed integer of the same + // width is implementation defined behavior if the source value would not fit + // in the destination type. We step around it with a roundtrip bitwise not + // operation to make sure this function remains constexpr. Clang and GCC + // optimize this to a no-op on x86-64. + return v & (static_cast(1) << 127) + ? ~static_cast<__int128>(~v) + : static_cast<__int128>(v); +} + +} // namespace int128_internal + +inline int128& int128::operator=(__int128 v) { + v_ = v; + return *this; +} + +constexpr uint64_t Int128Low64(int128 v) { + return static_cast(v.v_ & ~uint64_t{0}); +} + +constexpr int64_t Int128High64(int128 v) { + // Initially cast to unsigned to prevent a right shift on a negative value. + return int128_internal::BitCastToSigned( + static_cast(static_cast(v.v_) >> 64)); +} + +constexpr int128::int128(int64_t high, uint64_t low) + // Initially cast to unsigned to prevent a left shift that overflows. + : v_(int128_internal::BitCastToSigned(static_cast(high) + << 64) | + low) {} + + +constexpr int128::int128(int v) : v_{v} {} + +constexpr int128::int128(long v) : v_{v} {} // NOLINT(runtime/int) + +constexpr int128::int128(long long v) : v_{v} {} // NOLINT(runtime/int) + +constexpr int128::int128(__int128 v) : v_{v} {} + +constexpr int128::int128(unsigned int v) : v_{v} {} + +constexpr int128::int128(unsigned long v) : v_{v} {} // NOLINT(runtime/int) + +// NOLINTNEXTLINE(runtime/int) +constexpr int128::int128(unsigned long long v) : v_{v} {} + +constexpr int128::int128(unsigned __int128 v) : v_{static_cast<__int128>(v)} {} + +inline int128::int128(float v) { + v_ = static_cast<__int128>(v); +} + +inline int128::int128(double v) { + v_ = static_cast<__int128>(v); +} + +inline int128::int128(long double v) { + v_ = static_cast<__int128>(v); +} + +constexpr int128::int128(uint128 v) : v_{static_cast<__int128>(v)} {} + +constexpr int128::operator bool() const { return static_cast(v_); } + +constexpr int128::operator char() const { return static_cast(v_); } + +constexpr int128::operator signed char() const { + return static_cast(v_); +} + +constexpr int128::operator unsigned char() const { + return static_cast(v_); +} + +constexpr int128::operator char16_t() const { + return static_cast(v_); +} + +constexpr int128::operator char32_t() const { + return static_cast(v_); +} + +constexpr int128::operator ABSL_INTERNAL_WCHAR_T() const { + return static_cast(v_); +} + +constexpr int128::operator short() const { // NOLINT(runtime/int) + return static_cast(v_); // NOLINT(runtime/int) +} + +constexpr int128::operator unsigned short() const { // NOLINT(runtime/int) + return static_cast(v_); // NOLINT(runtime/int) +} + +constexpr int128::operator int() const { + return static_cast(v_); +} + +constexpr int128::operator unsigned int() const { + return static_cast(v_); +} + +constexpr int128::operator long() const { // NOLINT(runtime/int) + return static_cast(v_); // NOLINT(runtime/int) +} + +constexpr int128::operator unsigned long() const { // NOLINT(runtime/int) + return static_cast(v_); // NOLINT(runtime/int) +} + +constexpr int128::operator long long() const { // NOLINT(runtime/int) + return static_cast(v_); // NOLINT(runtime/int) +} + +constexpr int128::operator unsigned long long() const { // NOLINT(runtime/int) + return static_cast(v_); // NOLINT(runtime/int) +} + +constexpr int128::operator __int128() const { return v_; } + +constexpr int128::operator unsigned __int128() const { + return static_cast(v_); +} + +// Clang on PowerPC sometimes produces incorrect __int128 to floating point +// conversions. In that case, we do the conversion with a similar implementation +// to the conversion operators in int128_no_intrinsic.inc. +#if defined(__clang__) && !defined(__ppc64__) +inline int128::operator float() const { return static_cast(v_); } + +inline int128::operator double () const { return static_cast(v_); } + +inline int128::operator long double() const { + return static_cast(v_); +} + +#else // Clang on PowerPC +// Forward declaration for conversion operators to floating point types. +int128 operator-(int128 v); +bool operator!=(int128 lhs, int128 rhs); + +inline int128::operator float() const { + // We must convert the absolute value and then negate as needed, because + // floating point types are typically sign-magnitude. Otherwise, the + // difference between the high and low 64 bits when interpreted as two's + // complement overwhelms the precision of the mantissa. + // + // Also check to make sure we don't negate Int128Min() + return v_ < 0 && *this != Int128Min() + ? -static_cast(-*this) + : static_cast(Int128Low64(*this)) + + std::ldexp(static_cast(Int128High64(*this)), 64); +} + +inline int128::operator double() const { + // See comment in int128::operator float() above. + return v_ < 0 && *this != Int128Min() + ? -static_cast(-*this) + : static_cast(Int128Low64(*this)) + + std::ldexp(static_cast(Int128High64(*this)), 64); +} + +inline int128::operator long double() const { + // See comment in int128::operator float() above. + return v_ < 0 && *this != Int128Min() + ? -static_cast(-*this) + : static_cast(Int128Low64(*this)) + + std::ldexp(static_cast(Int128High64(*this)), + 64); +} +#endif // Clang on PowerPC + +// Comparison operators. + +inline bool operator==(int128 lhs, int128 rhs) { + return static_cast<__int128>(lhs) == static_cast<__int128>(rhs); +} + +inline bool operator!=(int128 lhs, int128 rhs) { + return static_cast<__int128>(lhs) != static_cast<__int128>(rhs); +} + +inline bool operator<(int128 lhs, int128 rhs) { + return static_cast<__int128>(lhs) < static_cast<__int128>(rhs); +} + +inline bool operator>(int128 lhs, int128 rhs) { + return static_cast<__int128>(lhs) > static_cast<__int128>(rhs); +} + +inline bool operator<=(int128 lhs, int128 rhs) { + return static_cast<__int128>(lhs) <= static_cast<__int128>(rhs); +} + +inline bool operator>=(int128 lhs, int128 rhs) { + return static_cast<__int128>(lhs) >= static_cast<__int128>(rhs); +} + +// Unary operators. + +inline int128 operator-(int128 v) { + return -static_cast<__int128>(v); +} + +inline bool operator!(int128 v) { + return !static_cast<__int128>(v); +} + +inline int128 operator~(int128 val) { + return ~static_cast<__int128>(val); +} + +// Arithmetic operators. + +inline int128 operator+(int128 lhs, int128 rhs) { + return static_cast<__int128>(lhs) + static_cast<__int128>(rhs); +} + +inline int128 operator-(int128 lhs, int128 rhs) { + return static_cast<__int128>(lhs) - static_cast<__int128>(rhs); +} + +inline int128 operator*(int128 lhs, int128 rhs) { + return static_cast<__int128>(lhs) * static_cast<__int128>(rhs); +} + +inline int128 operator/(int128 lhs, int128 rhs) { + return static_cast<__int128>(lhs) / static_cast<__int128>(rhs); +} + +inline int128 operator%(int128 lhs, int128 rhs) { + return static_cast<__int128>(lhs) % static_cast<__int128>(rhs); +} + +inline int128 int128::operator++(int) { + int128 tmp(*this); + ++v_; + return tmp; +} + +inline int128 int128::operator--(int) { + int128 tmp(*this); + --v_; + return tmp; +} + +inline int128& int128::operator++() { + ++v_; + return *this; +} + +inline int128& int128::operator--() { + --v_; + return *this; +} + +inline int128 operator|(int128 lhs, int128 rhs) { + return static_cast<__int128>(lhs) | static_cast<__int128>(rhs); +} + +inline int128 operator&(int128 lhs, int128 rhs) { + return static_cast<__int128>(lhs) & static_cast<__int128>(rhs); +} + +inline int128 operator^(int128 lhs, int128 rhs) { + return static_cast<__int128>(lhs) ^ static_cast<__int128>(rhs); +} + +inline int128 operator<<(int128 lhs, int amount) { + return static_cast<__int128>(lhs) << amount; +} + +inline int128 operator>>(int128 lhs, int amount) { + return static_cast<__int128>(lhs) >> amount; +} diff --git a/absl/numeric/int128_no_intrinsic.inc b/absl/numeric/int128_no_intrinsic.inc index 046cb9b3a40..c753771ae73 100644 --- a/absl/numeric/int128_no_intrinsic.inc +++ b/absl/numeric/int128_no_intrinsic.inc @@ -16,3 +16,293 @@ // This file contains :int128 implementation details that depend on internal // representation when ABSL_HAVE_INTRINSIC_INT128 is *not* defined. This file // is included by int128.h and relies on ABSL_INTERNAL_WCHAR_T being defined. + +constexpr uint64_t Int128Low64(int128 v) { return v.lo_; } + +constexpr int64_t Int128High64(int128 v) { return v.hi_; } + +#if defined(ABSL_IS_LITTLE_ENDIAN) + +constexpr int128::int128(int64_t high, uint64_t low) : + lo_(low), hi_(high) {} + +constexpr int128::int128(int v) + : lo_{static_cast(v)}, hi_{v < 0 ? ~int64_t{0} : 0} {} +constexpr int128::int128(long v) // NOLINT(runtime/int) + : lo_{static_cast(v)}, hi_{v < 0 ? ~int64_t{0} : 0} {} +constexpr int128::int128(long long v) // NOLINT(runtime/int) + : lo_{static_cast(v)}, hi_{v < 0 ? ~int64_t{0} : 0} {} + +constexpr int128::int128(unsigned int v) : lo_{v}, hi_{0} {} +// NOLINTNEXTLINE(runtime/int) +constexpr int128::int128(unsigned long v) : lo_{v}, hi_{0} {} +// NOLINTNEXTLINE(runtime/int) +constexpr int128::int128(unsigned long long v) : lo_{v}, hi_{0} {} + +constexpr int128::int128(uint128 v) + : lo_{Uint128Low64(v)}, hi_{static_cast(Uint128High64(v))} {} + +#elif defined(ABSL_IS_BIG_ENDIAN) + +constexpr int128::int128(int64_t high, uint64_t low) : + hi_{high}, lo_{low} {} + +constexpr int128::int128(int v) + : hi_{v < 0 ? ~int64_t{0} : 0}, lo_{static_cast(v)} {} +constexpr int128::int128(long v) // NOLINT(runtime/int) + : hi_{v < 0 ? ~int64_t{0} : 0}, lo_{static_cast(v)} {} +constexpr int128::int128(long long v) // NOLINT(runtime/int) + : hi_{v < 0 ? ~int64_t{0} : 0}, lo_{static_cast(v)} {} + +constexpr int128::int128(unsigned int v) : hi_{0}, lo_{v} {} +// NOLINTNEXTLINE(runtime/int) +constexpr int128::int128(unsigned long v) : hi_{0}, lo_{v} {} +// NOLINTNEXTLINE(runtime/int) +constexpr int128::int128(unsigned long long v) : hi_{0}, lo_{v} {} + +constexpr int128::int128(uint128 v) + : hi_{static_cast(Uint128High64(v))}, lo_{Uint128Low64(v)} {} + +#else // byte order +#error "Unsupported byte order: must be little-endian or big-endian." +#endif // byte order + +constexpr int128::operator bool() const { return lo_ || hi_; } + +constexpr int128::operator char() const { + // NOLINTNEXTLINE(runtime/int) + return static_cast(static_cast(*this)); +} + +constexpr int128::operator signed char() const { + // NOLINTNEXTLINE(runtime/int) + return static_cast(static_cast(*this)); +} + +constexpr int128::operator unsigned char() const { + return static_cast(lo_); +} + +constexpr int128::operator char16_t() const { + return static_cast(lo_); +} + +constexpr int128::operator char32_t() const { + return static_cast(lo_); +} + +constexpr int128::operator ABSL_INTERNAL_WCHAR_T() const { + // NOLINTNEXTLINE(runtime/int) + return static_cast(static_cast(*this)); +} + +constexpr int128::operator short() const { // NOLINT(runtime/int) + // NOLINTNEXTLINE(runtime/int) + return static_cast(static_cast(*this)); +} + +constexpr int128::operator unsigned short() const { // NOLINT(runtime/int) + return static_cast(lo_); // NOLINT(runtime/int) +} + +constexpr int128::operator int() const { + // NOLINTNEXTLINE(runtime/int) + return static_cast(static_cast(*this)); +} + +constexpr int128::operator unsigned int() const { + return static_cast(lo_); +} + +constexpr int128::operator long() const { // NOLINT(runtime/int) + // NOLINTNEXTLINE(runtime/int) + return static_cast(static_cast(*this)); +} + +constexpr int128::operator unsigned long() const { // NOLINT(runtime/int) + return static_cast(lo_); // NOLINT(runtime/int) +} + +constexpr int128::operator long long() const { // NOLINT(runtime/int) + // We don't bother checking the value of hi_. If *this < 0, lo_'s high bit + // must be set in order for the value to fit into a long long. Conversely, if + // lo_'s high bit is set, *this must be < 0 for the value to fit. + return int128_internal::BitCastToSigned(lo_); +} + +constexpr int128::operator unsigned long long() const { // NOLINT(runtime/int) + return static_cast(lo_); // NOLINT(runtime/int) +} + +// Forward declaration for conversion operators to floating point types. +int128 operator-(int128 v); +bool operator!=(int128 lhs, int128 rhs); + +inline int128::operator float() const { + // We must convert the absolute value and then negate as needed, because + // floating point types are typically sign-magnitude. Otherwise, the + // difference between the high and low 64 bits when interpreted as two's + // complement overwhelms the precision of the mantissa. + // + // Also check to make sure we don't negate Int128Min() + return hi_ < 0 && *this != Int128Min() + ? -static_cast(-*this) + : static_cast(lo_) + + std::ldexp(static_cast(hi_), 64); +} + +inline int128::operator double() const { + // See comment in int128::operator float() above. + return hi_ < 0 && *this != Int128Min() + ? -static_cast(-*this) + : static_cast(lo_) + + std::ldexp(static_cast(hi_), 64); +} + +inline int128::operator long double() const { + // See comment in int128::operator float() above. + return hi_ < 0 && *this != Int128Min() + ? -static_cast(-*this) + : static_cast(lo_) + + std::ldexp(static_cast(hi_), 64); +} + +// Comparison operators. + +inline bool operator==(int128 lhs, int128 rhs) { + return (Int128Low64(lhs) == Int128Low64(rhs) && + Int128High64(lhs) == Int128High64(rhs)); +} + +inline bool operator!=(int128 lhs, int128 rhs) { + return !(lhs == rhs); +} + +inline bool operator<(int128 lhs, int128 rhs) { + return (Int128High64(lhs) == Int128High64(rhs)) + ? (Int128Low64(lhs) < Int128Low64(rhs)) + : (Int128High64(lhs) < Int128High64(rhs)); +} + +inline bool operator>(int128 lhs, int128 rhs) { + return (Int128High64(lhs) == Int128High64(rhs)) + ? (Int128Low64(lhs) > Int128Low64(rhs)) + : (Int128High64(lhs) > Int128High64(rhs)); +} + +inline bool operator<=(int128 lhs, int128 rhs) { + return !(lhs > rhs); +} + +inline bool operator>=(int128 lhs, int128 rhs) { + return !(lhs < rhs); +} + +// Unary operators. + +inline int128 operator-(int128 v) { + int64_t hi = ~Int128High64(v); + uint64_t lo = ~Int128Low64(v) + 1; + if (lo == 0) ++hi; // carry + return MakeInt128(hi, lo); +} + +inline bool operator!(int128 v) { + return !Int128Low64(v) && !Int128High64(v); +} + +inline int128 operator~(int128 val) { + return MakeInt128(~Int128High64(val), ~Int128Low64(val)); +} + +// Arithmetic operators. + +inline int128 operator+(int128 lhs, int128 rhs) { + int128 result = MakeInt128(Int128High64(lhs) + Int128High64(rhs), + Int128Low64(lhs) + Int128Low64(rhs)); + if (Int128Low64(result) < Int128Low64(lhs)) { // check for carry + return MakeInt128(Int128High64(result) + 1, Int128Low64(result)); + } + return result; +} + +inline int128 operator-(int128 lhs, int128 rhs) { + int128 result = MakeInt128(Int128High64(lhs) - Int128High64(rhs), + Int128Low64(lhs) - Int128Low64(rhs)); + if (Int128Low64(lhs) < Int128Low64(rhs)) { // check for carry + return MakeInt128(Int128High64(result) - 1, Int128Low64(result)); + } + return result; +} + +inline int128 operator*(int128 lhs, int128 rhs) { + uint128 result = uint128(lhs) * rhs; + return MakeInt128(int128_internal::BitCastToSigned(Uint128High64(result)), + Uint128Low64(result)); +} + +inline int128 int128::operator++(int) { + int128 tmp(*this); + *this += 1; + return tmp; +} + +inline int128 int128::operator--(int) { + int128 tmp(*this); + *this -= 1; + return tmp; +} + +inline int128& int128::operator++() { + *this += 1; + return *this; +} + +inline int128& int128::operator--() { + *this -= 1; + return *this; +} + +inline int128 operator|(int128 lhs, int128 rhs) { + return MakeInt128(Int128High64(lhs) | Int128High64(rhs), + Int128Low64(lhs) | Int128Low64(rhs)); +} + +inline int128 operator&(int128 lhs, int128 rhs) { + return MakeInt128(Int128High64(lhs) & Int128High64(rhs), + Int128Low64(lhs) & Int128Low64(rhs)); +} + +inline int128 operator^(int128 lhs, int128 rhs) { + return MakeInt128(Int128High64(lhs) ^ Int128High64(rhs), + Int128Low64(lhs) ^ Int128Low64(rhs)); +} + +inline int128 operator<<(int128 lhs, int amount) { + // uint64_t shifts of >= 64 are undefined, so we need some special-casing. + if (amount < 64) { + if (amount != 0) { + return MakeInt128( + (Int128High64(lhs) << amount) | + static_cast(Int128Low64(lhs) >> (64 - amount)), + Int128Low64(lhs) << amount); + } + return lhs; + } + return MakeInt128(static_cast(Int128Low64(lhs) << (amount - 64)), 0); +} + +inline int128 operator>>(int128 lhs, int amount) { + // uint64_t shifts of >= 64 are undefined, so we need some special-casing. + if (amount < 64) { + if (amount != 0) { + return MakeInt128( + Int128High64(lhs) >> amount, + (Int128Low64(lhs) >> amount) | + (static_cast(Int128High64(lhs)) << (64 - amount))); + } + return lhs; + } + return MakeInt128(0, + static_cast(Int128High64(lhs) >> (amount - 64))); +} diff --git a/absl/numeric/int128_stream_test.cc b/absl/numeric/int128_stream_test.cc index 3cfa9dc1835..479ad66cf4d 100644 --- a/absl/numeric/int128_stream_test.cc +++ b/absl/numeric/int128_stream_test.cc @@ -147,6 +147,735 @@ TEST(Uint128, OStreamFormatTest) { } } +struct Int128TestCase { + absl::int128 value; + std::ios_base::fmtflags flags; + std::streamsize width; + const char* expected; +}; + +void CheckInt128Case(const Int128TestCase& test_case) { + std::ostringstream os; + os.flags(test_case.flags); + os.width(test_case.width); + os.fill(kFill); + os << test_case.value; + SCOPED_TRACE(StreamFormatToString(test_case.flags, test_case.width)); + EXPECT_EQ(test_case.expected, os.str()); +} + +TEST(Int128, OStreamValueTest) { + CheckInt128Case({1, kDec, /*width = */ 0, "1"}); + CheckInt128Case({1, kOct, /*width = */ 0, "1"}); + CheckInt128Case({1, kHex, /*width = */ 0, "1"}); + CheckInt128Case({9, kDec, /*width = */ 0, "9"}); + CheckInt128Case({9, kOct, /*width = */ 0, "11"}); + CheckInt128Case({9, kHex, /*width = */ 0, "9"}); + CheckInt128Case({12345, kDec, /*width = */ 0, "12345"}); + CheckInt128Case({12345, kOct, /*width = */ 0, "30071"}); + CheckInt128Case({12345, kHex, /*width = */ 0, "3039"}); + CheckInt128Case( + {0x8000000000000000, kDec, /*width = */ 0, "9223372036854775808"}); + CheckInt128Case( + {0x8000000000000000, kOct, /*width = */ 0, "1000000000000000000000"}); + CheckInt128Case( + {0x8000000000000000, kHex, /*width = */ 0, "8000000000000000"}); + CheckInt128Case({std::numeric_limits::max(), kDec, + /*width = */ 0, "18446744073709551615"}); + CheckInt128Case({std::numeric_limits::max(), kOct, + /*width = */ 0, "1777777777777777777777"}); + CheckInt128Case({std::numeric_limits::max(), kHex, + /*width = */ 0, "ffffffffffffffff"}); + CheckInt128Case( + {absl::MakeInt128(1, 0), kDec, /*width = */ 0, "18446744073709551616"}); + CheckInt128Case( + {absl::MakeInt128(1, 0), kOct, /*width = */ 0, "2000000000000000000000"}); + CheckInt128Case( + {absl::MakeInt128(1, 0), kHex, /*width = */ 0, "10000000000000000"}); + CheckInt128Case({absl::MakeInt128(std::numeric_limits::max(), + std::numeric_limits::max()), + std::ios::dec, /*width = */ 0, + "170141183460469231731687303715884105727"}); + CheckInt128Case({absl::MakeInt128(std::numeric_limits::max(), + std::numeric_limits::max()), + std::ios::oct, /*width = */ 0, + "1777777777777777777777777777777777777777777"}); + CheckInt128Case({absl::MakeInt128(std::numeric_limits::max(), + std::numeric_limits::max()), + std::ios::hex, /*width = */ 0, + "7fffffffffffffffffffffffffffffff"}); + CheckInt128Case({absl::MakeInt128(std::numeric_limits::min(), 0), + std::ios::dec, /*width = */ 0, + "-170141183460469231731687303715884105728"}); + CheckInt128Case({absl::MakeInt128(std::numeric_limits::min(), 0), + std::ios::oct, /*width = */ 0, + "2000000000000000000000000000000000000000000"}); + CheckInt128Case({absl::MakeInt128(std::numeric_limits::min(), 0), + std::ios::hex, /*width = */ 0, + "80000000000000000000000000000000"}); + CheckInt128Case({-1, std::ios::dec, /*width = */ 0, "-1"}); + CheckInt128Case({-1, std::ios::oct, /*width = */ 0, + "3777777777777777777777777777777777777777777"}); + CheckInt128Case( + {-1, std::ios::hex, /*width = */ 0, "ffffffffffffffffffffffffffffffff"}); + CheckInt128Case({-12345, std::ios::dec, /*width = */ 0, "-12345"}); + CheckInt128Case({-12345, std::ios::oct, /*width = */ 0, + "3777777777777777777777777777777777777747707"}); + CheckInt128Case({-12345, std::ios::hex, /*width = */ 0, + "ffffffffffffffffffffffffffffcfc7"}); +} + +std::vector GetInt128FormatCases(); +TEST(Int128, OStreamFormatTest) { + for (const Int128TestCase& test_case : GetInt128FormatCases()) { + CheckInt128Case(test_case); + } +} + +std::vector GetInt128FormatCases() { + return { + {0, std::ios_base::fmtflags(), /*width = */ 0, "0"}, + {0, std::ios_base::fmtflags(), /*width = */ 6, "_____0"}, + {0, kPos, /*width = */ 0, "+0"}, + {0, kPos, /*width = */ 6, "____+0"}, + {0, kBase, /*width = */ 0, "0"}, + {0, kBase, /*width = */ 6, "_____0"}, + {0, kBase | kPos, /*width = */ 0, "+0"}, + {0, kBase | kPos, /*width = */ 6, "____+0"}, + {0, kUpper, /*width = */ 0, "0"}, + {0, kUpper, /*width = */ 6, "_____0"}, + {0, kUpper | kPos, /*width = */ 0, "+0"}, + {0, kUpper | kPos, /*width = */ 6, "____+0"}, + {0, kUpper | kBase, /*width = */ 0, "0"}, + {0, kUpper | kBase, /*width = */ 6, "_____0"}, + {0, kUpper | kBase | kPos, /*width = */ 0, "+0"}, + {0, kUpper | kBase | kPos, /*width = */ 6, "____+0"}, + {0, kLeft, /*width = */ 0, "0"}, + {0, kLeft, /*width = */ 6, "0_____"}, + {0, kLeft | kPos, /*width = */ 0, "+0"}, + {0, kLeft | kPos, /*width = */ 6, "+0____"}, + {0, kLeft | kBase, /*width = */ 0, "0"}, + {0, kLeft | kBase, /*width = */ 6, "0_____"}, + {0, kLeft | kBase | kPos, /*width = */ 0, "+0"}, + {0, kLeft | kBase | kPos, /*width = */ 6, "+0____"}, + {0, kLeft | kUpper, /*width = */ 0, "0"}, + {0, kLeft | kUpper, /*width = */ 6, "0_____"}, + {0, kLeft | kUpper | kPos, /*width = */ 0, "+0"}, + {0, kLeft | kUpper | kPos, /*width = */ 6, "+0____"}, + {0, kLeft | kUpper | kBase, /*width = */ 0, "0"}, + {0, kLeft | kUpper | kBase, /*width = */ 6, "0_____"}, + {0, kLeft | kUpper | kBase | kPos, /*width = */ 0, "+0"}, + {0, kLeft | kUpper | kBase | kPos, /*width = */ 6, "+0____"}, + {0, kInt, /*width = */ 0, "0"}, + {0, kInt, /*width = */ 6, "_____0"}, + {0, kInt | kPos, /*width = */ 0, "+0"}, + {0, kInt | kPos, /*width = */ 6, "+____0"}, + {0, kInt | kBase, /*width = */ 0, "0"}, + {0, kInt | kBase, /*width = */ 6, "_____0"}, + {0, kInt | kBase | kPos, /*width = */ 0, "+0"}, + {0, kInt | kBase | kPos, /*width = */ 6, "+____0"}, + {0, kInt | kUpper, /*width = */ 0, "0"}, + {0, kInt | kUpper, /*width = */ 6, "_____0"}, + {0, kInt | kUpper | kPos, /*width = */ 0, "+0"}, + {0, kInt | kUpper | kPos, /*width = */ 6, "+____0"}, + {0, kInt | kUpper | kBase, /*width = */ 0, "0"}, + {0, kInt | kUpper | kBase, /*width = */ 6, "_____0"}, + {0, kInt | kUpper | kBase | kPos, /*width = */ 0, "+0"}, + {0, kInt | kUpper | kBase | kPos, /*width = */ 6, "+____0"}, + {0, kRight, /*width = */ 0, "0"}, + {0, kRight, /*width = */ 6, "_____0"}, + {0, kRight | kPos, /*width = */ 0, "+0"}, + {0, kRight | kPos, /*width = */ 6, "____+0"}, + {0, kRight | kBase, /*width = */ 0, "0"}, + {0, kRight | kBase, /*width = */ 6, "_____0"}, + {0, kRight | kBase | kPos, /*width = */ 0, "+0"}, + {0, kRight | kBase | kPos, /*width = */ 6, "____+0"}, + {0, kRight | kUpper, /*width = */ 0, "0"}, + {0, kRight | kUpper, /*width = */ 6, "_____0"}, + {0, kRight | kUpper | kPos, /*width = */ 0, "+0"}, + {0, kRight | kUpper | kPos, /*width = */ 6, "____+0"}, + {0, kRight | kUpper | kBase, /*width = */ 0, "0"}, + {0, kRight | kUpper | kBase, /*width = */ 6, "_____0"}, + {0, kRight | kUpper | kBase | kPos, /*width = */ 0, "+0"}, + {0, kRight | kUpper | kBase | kPos, /*width = */ 6, "____+0"}, + {0, kDec, /*width = */ 0, "0"}, + {0, kDec, /*width = */ 6, "_____0"}, + {0, kDec | kPos, /*width = */ 0, "+0"}, + {0, kDec | kPos, /*width = */ 6, "____+0"}, + {0, kDec | kBase, /*width = */ 0, "0"}, + {0, kDec | kBase, /*width = */ 6, "_____0"}, + {0, kDec | kBase | kPos, /*width = */ 0, "+0"}, + {0, kDec | kBase | kPos, /*width = */ 6, "____+0"}, + {0, kDec | kUpper, /*width = */ 0, "0"}, + {0, kDec | kUpper, /*width = */ 6, "_____0"}, + {0, kDec | kUpper | kPos, /*width = */ 0, "+0"}, + {0, kDec | kUpper | kPos, /*width = */ 6, "____+0"}, + {0, kDec | kUpper | kBase, /*width = */ 0, "0"}, + {0, kDec | kUpper | kBase, /*width = */ 6, "_____0"}, + {0, kDec | kUpper | kBase | kPos, /*width = */ 0, "+0"}, + {0, kDec | kUpper | kBase | kPos, /*width = */ 6, "____+0"}, + {0, kDec | kLeft, /*width = */ 0, "0"}, + {0, kDec | kLeft, /*width = */ 6, "0_____"}, + {0, kDec | kLeft | kPos, /*width = */ 0, "+0"}, + {0, kDec | kLeft | kPos, /*width = */ 6, "+0____"}, + {0, kDec | kLeft | kBase, /*width = */ 0, "0"}, + {0, kDec | kLeft | kBase, /*width = */ 6, "0_____"}, + {0, kDec | kLeft | kBase | kPos, /*width = */ 0, "+0"}, + {0, kDec | kLeft | kBase | kPos, /*width = */ 6, "+0____"}, + {0, kDec | kLeft | kUpper, /*width = */ 0, "0"}, + {0, kDec | kLeft | kUpper, /*width = */ 6, "0_____"}, + {0, kDec | kLeft | kUpper | kPos, /*width = */ 0, "+0"}, + {0, kDec | kLeft | kUpper | kPos, /*width = */ 6, "+0____"}, + {0, kDec | kLeft | kUpper | kBase, /*width = */ 0, "0"}, + {0, kDec | kLeft | kUpper | kBase, /*width = */ 6, "0_____"}, + {0, kDec | kLeft | kUpper | kBase | kPos, /*width = */ 0, "+0"}, + {0, kDec | kLeft | kUpper | kBase | kPos, /*width = */ 6, "+0____"}, + {0, kDec | kInt, /*width = */ 0, "0"}, + {0, kDec | kInt, /*width = */ 6, "_____0"}, + {0, kDec | kInt | kPos, /*width = */ 0, "+0"}, + {0, kDec | kInt | kPos, /*width = */ 6, "+____0"}, + {0, kDec | kInt | kBase, /*width = */ 0, "0"}, + {0, kDec | kInt | kBase, /*width = */ 6, "_____0"}, + {0, kDec | kInt | kBase | kPos, /*width = */ 0, "+0"}, + {0, kDec | kInt | kBase | kPos, /*width = */ 6, "+____0"}, + {0, kDec | kInt | kUpper, /*width = */ 0, "0"}, + {0, kDec | kInt | kUpper, /*width = */ 6, "_____0"}, + {0, kDec | kInt | kUpper | kPos, /*width = */ 0, "+0"}, + {0, kDec | kInt | kUpper | kPos, /*width = */ 6, "+____0"}, + {0, kDec | kInt | kUpper | kBase, /*width = */ 0, "0"}, + {0, kDec | kInt | kUpper | kBase, /*width = */ 6, "_____0"}, + {0, kDec | kInt | kUpper | kBase | kPos, /*width = */ 0, "+0"}, + {0, kDec | kInt | kUpper | kBase | kPos, /*width = */ 6, "+____0"}, + {0, kDec | kRight, /*width = */ 0, "0"}, + {0, kDec | kRight, /*width = */ 6, "_____0"}, + {0, kDec | kRight | kPos, /*width = */ 0, "+0"}, + {0, kDec | kRight | kPos, /*width = */ 6, "____+0"}, + {0, kDec | kRight | kBase, /*width = */ 0, "0"}, + {0, kDec | kRight | kBase, /*width = */ 6, "_____0"}, + {0, kDec | kRight | kBase | kPos, /*width = */ 0, "+0"}, + {0, kDec | kRight | kBase | kPos, /*width = */ 6, "____+0"}, + {0, kDec | kRight | kUpper, /*width = */ 0, "0"}, + {0, kDec | kRight | kUpper, /*width = */ 6, "_____0"}, + {0, kDec | kRight | kUpper | kPos, /*width = */ 0, "+0"}, + {0, kDec | kRight | kUpper | kPos, /*width = */ 6, "____+0"}, + {0, kDec | kRight | kUpper | kBase, /*width = */ 0, "0"}, + {0, kDec | kRight | kUpper | kBase, /*width = */ 6, "_____0"}, + {0, kDec | kRight | kUpper | kBase | kPos, /*width = */ 0, "+0"}, + {0, kDec | kRight | kUpper | kBase | kPos, /*width = */ 6, "____+0"}, + {0, kOct, /*width = */ 0, "0"}, + {0, kOct, /*width = */ 6, "_____0"}, + {0, kOct | kPos, /*width = */ 0, "0"}, + {0, kOct | kPos, /*width = */ 6, "_____0"}, + {0, kOct | kBase, /*width = */ 0, "0"}, + {0, kOct | kBase, /*width = */ 6, "_____0"}, + {0, kOct | kBase | kPos, /*width = */ 0, "0"}, + {0, kOct | kBase | kPos, /*width = */ 6, "_____0"}, + {0, kOct | kUpper, /*width = */ 0, "0"}, + {0, kOct | kUpper, /*width = */ 6, "_____0"}, + {0, kOct | kUpper | kPos, /*width = */ 0, "0"}, + {0, kOct | kUpper | kPos, /*width = */ 6, "_____0"}, + {0, kOct | kUpper | kBase, /*width = */ 0, "0"}, + {0, kOct | kUpper | kBase, /*width = */ 6, "_____0"}, + {0, kOct | kUpper | kBase | kPos, /*width = */ 0, "0"}, + {0, kOct | kUpper | kBase | kPos, /*width = */ 6, "_____0"}, + {0, kOct | kLeft, /*width = */ 0, "0"}, + {0, kOct | kLeft, /*width = */ 6, "0_____"}, + {0, kOct | kLeft | kPos, /*width = */ 0, "0"}, + {0, kOct | kLeft | kPos, /*width = */ 6, "0_____"}, + {0, kOct | kLeft | kBase, /*width = */ 0, "0"}, + {0, kOct | kLeft | kBase, /*width = */ 6, "0_____"}, + {0, kOct | kLeft | kBase | kPos, /*width = */ 0, "0"}, + {0, kOct | kLeft | kBase | kPos, /*width = */ 6, "0_____"}, + {0, kOct | kLeft | kUpper, /*width = */ 0, "0"}, + {0, kOct | kLeft | kUpper, /*width = */ 6, "0_____"}, + {0, kOct | kLeft | kUpper | kPos, /*width = */ 0, "0"}, + {0, kOct | kLeft | kUpper | kPos, /*width = */ 6, "0_____"}, + {0, kOct | kLeft | kUpper | kBase, /*width = */ 0, "0"}, + {0, kOct | kLeft | kUpper | kBase, /*width = */ 6, "0_____"}, + {0, kOct | kLeft | kUpper | kBase | kPos, /*width = */ 0, "0"}, + {0, kOct | kLeft | kUpper | kBase | kPos, /*width = */ 6, "0_____"}, + {0, kOct | kInt, /*width = */ 0, "0"}, + {0, kOct | kInt, /*width = */ 6, "_____0"}, + {0, kOct | kInt | kPos, /*width = */ 0, "0"}, + {0, kOct | kInt | kPos, /*width = */ 6, "_____0"}, + {0, kOct | kInt | kBase, /*width = */ 0, "0"}, + {0, kOct | kInt | kBase, /*width = */ 6, "_____0"}, + {0, kOct | kInt | kBase | kPos, /*width = */ 0, "0"}, + {0, kOct | kInt | kBase | kPos, /*width = */ 6, "_____0"}, + {0, kOct | kInt | kUpper, /*width = */ 0, "0"}, + {0, kOct | kInt | kUpper, /*width = */ 6, "_____0"}, + {0, kOct | kInt | kUpper | kPos, /*width = */ 0, "0"}, + {0, kOct | kInt | kUpper | kPos, /*width = */ 6, "_____0"}, + {0, kOct | kInt | kUpper | kBase, /*width = */ 0, "0"}, + {0, kOct | kInt | kUpper | kBase, /*width = */ 6, "_____0"}, + {0, kOct | kInt | kUpper | kBase | kPos, /*width = */ 0, "0"}, + {0, kOct | kInt | kUpper | kBase | kPos, /*width = */ 6, "_____0"}, + {0, kOct | kRight, /*width = */ 0, "0"}, + {0, kOct | kRight, /*width = */ 6, "_____0"}, + {0, kOct | kRight | kPos, /*width = */ 0, "0"}, + {0, kOct | kRight | kPos, /*width = */ 6, "_____0"}, + {0, kOct | kRight | kBase, /*width = */ 0, "0"}, + {0, kOct | kRight | kBase, /*width = */ 6, "_____0"}, + {0, kOct | kRight | kBase | kPos, /*width = */ 0, "0"}, + {0, kOct | kRight | kBase | kPos, /*width = */ 6, "_____0"}, + {0, kOct | kRight | kUpper, /*width = */ 0, "0"}, + {0, kOct | kRight | kUpper, /*width = */ 6, "_____0"}, + {0, kOct | kRight | kUpper | kPos, /*width = */ 0, "0"}, + {0, kOct | kRight | kUpper | kPos, /*width = */ 6, "_____0"}, + {0, kOct | kRight | kUpper | kBase, /*width = */ 0, "0"}, + {0, kOct | kRight | kUpper | kBase, /*width = */ 6, "_____0"}, + {0, kOct | kRight | kUpper | kBase | kPos, /*width = */ 0, "0"}, + {0, kOct | kRight | kUpper | kBase | kPos, /*width = */ 6, "_____0"}, + {0, kHex, /*width = */ 0, "0"}, + {0, kHex, /*width = */ 6, "_____0"}, + {0, kHex | kPos, /*width = */ 0, "0"}, + {0, kHex | kPos, /*width = */ 6, "_____0"}, + {0, kHex | kBase, /*width = */ 0, "0"}, + {0, kHex | kBase, /*width = */ 6, "_____0"}, + {0, kHex | kBase | kPos, /*width = */ 0, "0"}, + {0, kHex | kBase | kPos, /*width = */ 6, "_____0"}, + {0, kHex | kUpper, /*width = */ 0, "0"}, + {0, kHex | kUpper, /*width = */ 6, "_____0"}, + {0, kHex | kUpper | kPos, /*width = */ 0, "0"}, + {0, kHex | kUpper | kPos, /*width = */ 6, "_____0"}, + {0, kHex | kUpper | kBase, /*width = */ 0, "0"}, + {0, kHex | kUpper | kBase, /*width = */ 6, "_____0"}, + {0, kHex | kUpper | kBase | kPos, /*width = */ 0, "0"}, + {0, kHex | kUpper | kBase | kPos, /*width = */ 6, "_____0"}, + {0, kHex | kLeft, /*width = */ 0, "0"}, + {0, kHex | kLeft, /*width = */ 6, "0_____"}, + {0, kHex | kLeft | kPos, /*width = */ 0, "0"}, + {0, kHex | kLeft | kPos, /*width = */ 6, "0_____"}, + {0, kHex | kLeft | kBase, /*width = */ 0, "0"}, + {0, kHex | kLeft | kBase, /*width = */ 6, "0_____"}, + {0, kHex | kLeft | kBase | kPos, /*width = */ 0, "0"}, + {0, kHex | kLeft | kBase | kPos, /*width = */ 6, "0_____"}, + {0, kHex | kLeft | kUpper, /*width = */ 0, "0"}, + {0, kHex | kLeft | kUpper, /*width = */ 6, "0_____"}, + {0, kHex | kLeft | kUpper | kPos, /*width = */ 0, "0"}, + {0, kHex | kLeft | kUpper | kPos, /*width = */ 6, "0_____"}, + {0, kHex | kLeft | kUpper | kBase, /*width = */ 0, "0"}, + {0, kHex | kLeft | kUpper | kBase, /*width = */ 6, "0_____"}, + {0, kHex | kLeft | kUpper | kBase | kPos, /*width = */ 0, "0"}, + {0, kHex | kLeft | kUpper | kBase | kPos, /*width = */ 6, "0_____"}, + {0, kHex | kInt, /*width = */ 0, "0"}, + {0, kHex | kInt, /*width = */ 6, "_____0"}, + {0, kHex | kInt | kPos, /*width = */ 0, "0"}, + {0, kHex | kInt | kPos, /*width = */ 6, "_____0"}, + {0, kHex | kInt | kBase, /*width = */ 0, "0"}, + {0, kHex | kInt | kBase, /*width = */ 6, "_____0"}, + {0, kHex | kInt | kBase | kPos, /*width = */ 0, "0"}, + {0, kHex | kInt | kBase | kPos, /*width = */ 6, "_____0"}, + {0, kHex | kInt | kUpper, /*width = */ 0, "0"}, + {0, kHex | kInt | kUpper, /*width = */ 6, "_____0"}, + {0, kHex | kInt | kUpper | kPos, /*width = */ 0, "0"}, + {0, kHex | kInt | kUpper | kPos, /*width = */ 6, "_____0"}, + {0, kHex | kInt | kUpper | kBase, /*width = */ 0, "0"}, + {0, kHex | kInt | kUpper | kBase, /*width = */ 6, "_____0"}, + {0, kHex | kInt | kUpper | kBase | kPos, /*width = */ 0, "0"}, + {0, kHex | kInt | kUpper | kBase | kPos, /*width = */ 6, "_____0"}, + {0, kHex | kRight, /*width = */ 0, "0"}, + {0, kHex | kRight, /*width = */ 6, "_____0"}, + {0, kHex | kRight | kPos, /*width = */ 0, "0"}, + {0, kHex | kRight | kPos, /*width = */ 6, "_____0"}, + {0, kHex | kRight | kBase, /*width = */ 0, "0"}, + {0, kHex | kRight | kBase, /*width = */ 6, "_____0"}, + {0, kHex | kRight | kBase | kPos, /*width = */ 0, "0"}, + {0, kHex | kRight | kBase | kPos, /*width = */ 6, "_____0"}, + {0, kHex | kRight | kUpper, /*width = */ 0, "0"}, + {0, kHex | kRight | kUpper, /*width = */ 6, "_____0"}, + {0, kHex | kRight | kUpper | kPos, /*width = */ 0, "0"}, + {0, kHex | kRight | kUpper | kPos, /*width = */ 6, "_____0"}, + {0, kHex | kRight | kUpper | kBase, /*width = */ 0, "0"}, + {0, kHex | kRight | kUpper | kBase, /*width = */ 6, "_____0"}, + {0, kHex | kRight | kUpper | kBase | kPos, /*width = */ 0, "0"}, + {0, kHex | kRight | kUpper | kBase | kPos, /*width = */ 6, "_____0"}, + {42, std::ios_base::fmtflags(), /*width = */ 0, "42"}, + {42, std::ios_base::fmtflags(), /*width = */ 6, "____42"}, + {42, kPos, /*width = */ 0, "+42"}, + {42, kPos, /*width = */ 6, "___+42"}, + {42, kBase, /*width = */ 0, "42"}, + {42, kBase, /*width = */ 6, "____42"}, + {42, kBase | kPos, /*width = */ 0, "+42"}, + {42, kBase | kPos, /*width = */ 6, "___+42"}, + {42, kUpper, /*width = */ 0, "42"}, + {42, kUpper, /*width = */ 6, "____42"}, + {42, kUpper | kPos, /*width = */ 0, "+42"}, + {42, kUpper | kPos, /*width = */ 6, "___+42"}, + {42, kUpper | kBase, /*width = */ 0, "42"}, + {42, kUpper | kBase, /*width = */ 6, "____42"}, + {42, kUpper | kBase | kPos, /*width = */ 0, "+42"}, + {42, kUpper | kBase | kPos, /*width = */ 6, "___+42"}, + {42, kLeft, /*width = */ 0, "42"}, + {42, kLeft, /*width = */ 6, "42____"}, + {42, kLeft | kPos, /*width = */ 0, "+42"}, + {42, kLeft | kPos, /*width = */ 6, "+42___"}, + {42, kLeft | kBase, /*width = */ 0, "42"}, + {42, kLeft | kBase, /*width = */ 6, "42____"}, + {42, kLeft | kBase | kPos, /*width = */ 0, "+42"}, + {42, kLeft | kBase | kPos, /*width = */ 6, "+42___"}, + {42, kLeft | kUpper, /*width = */ 0, "42"}, + {42, kLeft | kUpper, /*width = */ 6, "42____"}, + {42, kLeft | kUpper | kPos, /*width = */ 0, "+42"}, + {42, kLeft | kUpper | kPos, /*width = */ 6, "+42___"}, + {42, kLeft | kUpper | kBase, /*width = */ 0, "42"}, + {42, kLeft | kUpper | kBase, /*width = */ 6, "42____"}, + {42, kLeft | kUpper | kBase | kPos, /*width = */ 0, "+42"}, + {42, kLeft | kUpper | kBase | kPos, /*width = */ 6, "+42___"}, + {42, kInt, /*width = */ 0, "42"}, + {42, kInt, /*width = */ 6, "____42"}, + {42, kInt | kPos, /*width = */ 0, "+42"}, + {42, kInt | kPos, /*width = */ 6, "+___42"}, + {42, kInt | kBase, /*width = */ 0, "42"}, + {42, kInt | kBase, /*width = */ 6, "____42"}, + {42, kInt | kBase | kPos, /*width = */ 0, "+42"}, + {42, kInt | kBase | kPos, /*width = */ 6, "+___42"}, + {42, kInt | kUpper, /*width = */ 0, "42"}, + {42, kInt | kUpper, /*width = */ 6, "____42"}, + {42, kInt | kUpper | kPos, /*width = */ 0, "+42"}, + {42, kInt | kUpper | kPos, /*width = */ 6, "+___42"}, + {42, kInt | kUpper | kBase, /*width = */ 0, "42"}, + {42, kInt | kUpper | kBase, /*width = */ 6, "____42"}, + {42, kInt | kUpper | kBase | kPos, /*width = */ 0, "+42"}, + {42, kInt | kUpper | kBase | kPos, /*width = */ 6, "+___42"}, + {42, kRight, /*width = */ 0, "42"}, + {42, kRight, /*width = */ 6, "____42"}, + {42, kRight | kPos, /*width = */ 0, "+42"}, + {42, kRight | kPos, /*width = */ 6, "___+42"}, + {42, kRight | kBase, /*width = */ 0, "42"}, + {42, kRight | kBase, /*width = */ 6, "____42"}, + {42, kRight | kBase | kPos, /*width = */ 0, "+42"}, + {42, kRight | kBase | kPos, /*width = */ 6, "___+42"}, + {42, kRight | kUpper, /*width = */ 0, "42"}, + {42, kRight | kUpper, /*width = */ 6, "____42"}, + {42, kRight | kUpper | kPos, /*width = */ 0, "+42"}, + {42, kRight | kUpper | kPos, /*width = */ 6, "___+42"}, + {42, kRight | kUpper | kBase, /*width = */ 0, "42"}, + {42, kRight | kUpper | kBase, /*width = */ 6, "____42"}, + {42, kRight | kUpper | kBase | kPos, /*width = */ 0, "+42"}, + {42, kRight | kUpper | kBase | kPos, /*width = */ 6, "___+42"}, + {42, kDec, /*width = */ 0, "42"}, + {42, kDec, /*width = */ 6, "____42"}, + {42, kDec | kPos, /*width = */ 0, "+42"}, + {42, kDec | kPos, /*width = */ 6, "___+42"}, + {42, kDec | kBase, /*width = */ 0, "42"}, + {42, kDec | kBase, /*width = */ 6, "____42"}, + {42, kDec | kBase | kPos, /*width = */ 0, "+42"}, + {42, kDec | kBase | kPos, /*width = */ 6, "___+42"}, + {42, kDec | kUpper, /*width = */ 0, "42"}, + {42, kDec | kUpper, /*width = */ 6, "____42"}, + {42, kDec | kUpper | kPos, /*width = */ 0, "+42"}, + {42, kDec | kUpper | kPos, /*width = */ 6, "___+42"}, + {42, kDec | kUpper | kBase, /*width = */ 0, "42"}, + {42, kDec | kUpper | kBase, /*width = */ 6, "____42"}, + {42, kDec | kUpper | kBase | kPos, /*width = */ 0, "+42"}, + {42, kDec | kUpper | kBase | kPos, /*width = */ 6, "___+42"}, + {42, kDec | kLeft, /*width = */ 0, "42"}, + {42, kDec | kLeft, /*width = */ 6, "42____"}, + {42, kDec | kLeft | kPos, /*width = */ 0, "+42"}, + {42, kDec | kLeft | kPos, /*width = */ 6, "+42___"}, + {42, kDec | kLeft | kBase, /*width = */ 0, "42"}, + {42, kDec | kLeft | kBase, /*width = */ 6, "42____"}, + {42, kDec | kLeft | kBase | kPos, /*width = */ 0, "+42"}, + {42, kDec | kLeft | kBase | kPos, /*width = */ 6, "+42___"}, + {42, kDec | kLeft | kUpper, /*width = */ 0, "42"}, + {42, kDec | kLeft | kUpper, /*width = */ 6, "42____"}, + {42, kDec | kLeft | kUpper | kPos, /*width = */ 0, "+42"}, + {42, kDec | kLeft | kUpper | kPos, /*width = */ 6, "+42___"}, + {42, kDec | kLeft | kUpper | kBase, /*width = */ 0, "42"}, + {42, kDec | kLeft | kUpper | kBase, /*width = */ 6, "42____"}, + {42, kDec | kLeft | kUpper | kBase | kPos, /*width = */ 0, "+42"}, + {42, kDec | kLeft | kUpper | kBase | kPos, /*width = */ 6, "+42___"}, + {42, kDec | kInt, /*width = */ 0, "42"}, + {42, kDec | kInt, /*width = */ 6, "____42"}, + {42, kDec | kInt | kPos, /*width = */ 0, "+42"}, + {42, kDec | kInt | kPos, /*width = */ 6, "+___42"}, + {42, kDec | kInt | kBase, /*width = */ 0, "42"}, + {42, kDec | kInt | kBase, /*width = */ 6, "____42"}, + {42, kDec | kInt | kBase | kPos, /*width = */ 0, "+42"}, + {42, kDec | kInt | kBase | kPos, /*width = */ 6, "+___42"}, + {42, kDec | kInt | kUpper, /*width = */ 0, "42"}, + {42, kDec | kInt | kUpper, /*width = */ 6, "____42"}, + {42, kDec | kInt | kUpper | kPos, /*width = */ 0, "+42"}, + {42, kDec | kInt | kUpper | kPos, /*width = */ 6, "+___42"}, + {42, kDec | kInt | kUpper | kBase, /*width = */ 0, "42"}, + {42, kDec | kInt | kUpper | kBase, /*width = */ 6, "____42"}, + {42, kDec | kInt | kUpper | kBase | kPos, /*width = */ 0, "+42"}, + {42, kDec | kInt | kUpper | kBase | kPos, /*width = */ 6, "+___42"}, + {42, kDec | kRight, /*width = */ 0, "42"}, + {42, kDec | kRight, /*width = */ 6, "____42"}, + {42, kDec | kRight | kPos, /*width = */ 0, "+42"}, + {42, kDec | kRight | kPos, /*width = */ 6, "___+42"}, + {42, kDec | kRight | kBase, /*width = */ 0, "42"}, + {42, kDec | kRight | kBase, /*width = */ 6, "____42"}, + {42, kDec | kRight | kBase | kPos, /*width = */ 0, "+42"}, + {42, kDec | kRight | kBase | kPos, /*width = */ 6, "___+42"}, + {42, kDec | kRight | kUpper, /*width = */ 0, "42"}, + {42, kDec | kRight | kUpper, /*width = */ 6, "____42"}, + {42, kDec | kRight | kUpper | kPos, /*width = */ 0, "+42"}, + {42, kDec | kRight | kUpper | kPos, /*width = */ 6, "___+42"}, + {42, kDec | kRight | kUpper | kBase, /*width = */ 0, "42"}, + {42, kDec | kRight | kUpper | kBase, /*width = */ 6, "____42"}, + {42, kDec | kRight | kUpper | kBase | kPos, /*width = */ 0, "+42"}, + {42, kDec | kRight | kUpper | kBase | kPos, /*width = */ 6, "___+42"}, + {42, kOct, /*width = */ 0, "52"}, + {42, kOct, /*width = */ 6, "____52"}, + {42, kOct | kPos, /*width = */ 0, "52"}, + {42, kOct | kPos, /*width = */ 6, "____52"}, + {42, kOct | kBase, /*width = */ 0, "052"}, + {42, kOct | kBase, /*width = */ 6, "___052"}, + {42, kOct | kBase | kPos, /*width = */ 0, "052"}, + {42, kOct | kBase | kPos, /*width = */ 6, "___052"}, + {42, kOct | kUpper, /*width = */ 0, "52"}, + {42, kOct | kUpper, /*width = */ 6, "____52"}, + {42, kOct | kUpper | kPos, /*width = */ 0, "52"}, + {42, kOct | kUpper | kPos, /*width = */ 6, "____52"}, + {42, kOct | kUpper | kBase, /*width = */ 0, "052"}, + {42, kOct | kUpper | kBase, /*width = */ 6, "___052"}, + {42, kOct | kUpper | kBase | kPos, /*width = */ 0, "052"}, + {42, kOct | kUpper | kBase | kPos, /*width = */ 6, "___052"}, + {42, kOct | kLeft, /*width = */ 0, "52"}, + {42, kOct | kLeft, /*width = */ 6, "52____"}, + {42, kOct | kLeft | kPos, /*width = */ 0, "52"}, + {42, kOct | kLeft | kPos, /*width = */ 6, "52____"}, + {42, kOct | kLeft | kBase, /*width = */ 0, "052"}, + {42, kOct | kLeft | kBase, /*width = */ 6, "052___"}, + {42, kOct | kLeft | kBase | kPos, /*width = */ 0, "052"}, + {42, kOct | kLeft | kBase | kPos, /*width = */ 6, "052___"}, + {42, kOct | kLeft | kUpper, /*width = */ 0, "52"}, + {42, kOct | kLeft | kUpper, /*width = */ 6, "52____"}, + {42, kOct | kLeft | kUpper | kPos, /*width = */ 0, "52"}, + {42, kOct | kLeft | kUpper | kPos, /*width = */ 6, "52____"}, + {42, kOct | kLeft | kUpper | kBase, /*width = */ 0, "052"}, + {42, kOct | kLeft | kUpper | kBase, /*width = */ 6, "052___"}, + {42, kOct | kLeft | kUpper | kBase | kPos, /*width = */ 0, "052"}, + {42, kOct | kLeft | kUpper | kBase | kPos, /*width = */ 6, "052___"}, + {42, kOct | kInt, /*width = */ 0, "52"}, + {42, kOct | kInt, /*width = */ 6, "____52"}, + {42, kOct | kInt | kPos, /*width = */ 0, "52"}, + {42, kOct | kInt | kPos, /*width = */ 6, "____52"}, + {42, kOct | kInt | kBase, /*width = */ 0, "052"}, + {42, kOct | kInt | kBase, /*width = */ 6, "___052"}, + {42, kOct | kInt | kBase | kPos, /*width = */ 0, "052"}, + {42, kOct | kInt | kBase | kPos, /*width = */ 6, "___052"}, + {42, kOct | kInt | kUpper, /*width = */ 0, "52"}, + {42, kOct | kInt | kUpper, /*width = */ 6, "____52"}, + {42, kOct | kInt | kUpper | kPos, /*width = */ 0, "52"}, + {42, kOct | kInt | kUpper | kPos, /*width = */ 6, "____52"}, + {42, kOct | kInt | kUpper | kBase, /*width = */ 0, "052"}, + {42, kOct | kInt | kUpper | kBase, /*width = */ 6, "___052"}, + {42, kOct | kInt | kUpper | kBase | kPos, /*width = */ 0, "052"}, + {42, kOct | kInt | kUpper | kBase | kPos, /*width = */ 6, "___052"}, + {42, kOct | kRight, /*width = */ 0, "52"}, + {42, kOct | kRight, /*width = */ 6, "____52"}, + {42, kOct | kRight | kPos, /*width = */ 0, "52"}, + {42, kOct | kRight | kPos, /*width = */ 6, "____52"}, + {42, kOct | kRight | kBase, /*width = */ 0, "052"}, + {42, kOct | kRight | kBase, /*width = */ 6, "___052"}, + {42, kOct | kRight | kBase | kPos, /*width = */ 0, "052"}, + {42, kOct | kRight | kBase | kPos, /*width = */ 6, "___052"}, + {42, kOct | kRight | kUpper, /*width = */ 0, "52"}, + {42, kOct | kRight | kUpper, /*width = */ 6, "____52"}, + {42, kOct | kRight | kUpper | kPos, /*width = */ 0, "52"}, + {42, kOct | kRight | kUpper | kPos, /*width = */ 6, "____52"}, + {42, kOct | kRight | kUpper | kBase, /*width = */ 0, "052"}, + {42, kOct | kRight | kUpper | kBase, /*width = */ 6, "___052"}, + {42, kOct | kRight | kUpper | kBase | kPos, /*width = */ 0, "052"}, + {42, kOct | kRight | kUpper | kBase | kPos, /*width = */ 6, "___052"}, + {42, kHex, /*width = */ 0, "2a"}, + {42, kHex, /*width = */ 6, "____2a"}, + {42, kHex | kPos, /*width = */ 0, "2a"}, + {42, kHex | kPos, /*width = */ 6, "____2a"}, + {42, kHex | kBase, /*width = */ 0, "0x2a"}, + {42, kHex | kBase, /*width = */ 6, "__0x2a"}, + {42, kHex | kBase | kPos, /*width = */ 0, "0x2a"}, + {42, kHex | kBase | kPos, /*width = */ 6, "__0x2a"}, + {42, kHex | kUpper, /*width = */ 0, "2A"}, + {42, kHex | kUpper, /*width = */ 6, "____2A"}, + {42, kHex | kUpper | kPos, /*width = */ 0, "2A"}, + {42, kHex | kUpper | kPos, /*width = */ 6, "____2A"}, + {42, kHex | kUpper | kBase, /*width = */ 0, "0X2A"}, + {42, kHex | kUpper | kBase, /*width = */ 6, "__0X2A"}, + {42, kHex | kUpper | kBase | kPos, /*width = */ 0, "0X2A"}, + {42, kHex | kUpper | kBase | kPos, /*width = */ 6, "__0X2A"}, + {42, kHex | kLeft, /*width = */ 0, "2a"}, + {42, kHex | kLeft, /*width = */ 6, "2a____"}, + {42, kHex | kLeft | kPos, /*width = */ 0, "2a"}, + {42, kHex | kLeft | kPos, /*width = */ 6, "2a____"}, + {42, kHex | kLeft | kBase, /*width = */ 0, "0x2a"}, + {42, kHex | kLeft | kBase, /*width = */ 6, "0x2a__"}, + {42, kHex | kLeft | kBase | kPos, /*width = */ 0, "0x2a"}, + {42, kHex | kLeft | kBase | kPos, /*width = */ 6, "0x2a__"}, + {42, kHex | kLeft | kUpper, /*width = */ 0, "2A"}, + {42, kHex | kLeft | kUpper, /*width = */ 6, "2A____"}, + {42, kHex | kLeft | kUpper | kPos, /*width = */ 0, "2A"}, + {42, kHex | kLeft | kUpper | kPos, /*width = */ 6, "2A____"}, + {42, kHex | kLeft | kUpper | kBase, /*width = */ 0, "0X2A"}, + {42, kHex | kLeft | kUpper | kBase, /*width = */ 6, "0X2A__"}, + {42, kHex | kLeft | kUpper | kBase | kPos, /*width = */ 0, "0X2A"}, + {42, kHex | kLeft | kUpper | kBase | kPos, /*width = */ 6, "0X2A__"}, + {42, kHex | kInt, /*width = */ 0, "2a"}, + {42, kHex | kInt, /*width = */ 6, "____2a"}, + {42, kHex | kInt | kPos, /*width = */ 0, "2a"}, + {42, kHex | kInt | kPos, /*width = */ 6, "____2a"}, + {42, kHex | kInt | kBase, /*width = */ 0, "0x2a"}, + {42, kHex | kInt | kBase, /*width = */ 6, "0x__2a"}, + {42, kHex | kInt | kBase | kPos, /*width = */ 0, "0x2a"}, + {42, kHex | kInt | kBase | kPos, /*width = */ 6, "0x__2a"}, + {42, kHex | kInt | kUpper, /*width = */ 0, "2A"}, + {42, kHex | kInt | kUpper, /*width = */ 6, "____2A"}, + {42, kHex | kInt | kUpper | kPos, /*width = */ 0, "2A"}, + {42, kHex | kInt | kUpper | kPos, /*width = */ 6, "____2A"}, + {42, kHex | kInt | kUpper | kBase, /*width = */ 0, "0X2A"}, + {42, kHex | kInt | kUpper | kBase, /*width = */ 6, "0X__2A"}, + {42, kHex | kInt | kUpper | kBase | kPos, /*width = */ 0, "0X2A"}, + {42, kHex | kInt | kUpper | kBase | kPos, /*width = */ 6, "0X__2A"}, + {42, kHex | kRight, /*width = */ 0, "2a"}, + {42, kHex | kRight, /*width = */ 6, "____2a"}, + {42, kHex | kRight | kPos, /*width = */ 0, "2a"}, + {42, kHex | kRight | kPos, /*width = */ 6, "____2a"}, + {42, kHex | kRight | kBase, /*width = */ 0, "0x2a"}, + {42, kHex | kRight | kBase, /*width = */ 6, "__0x2a"}, + {42, kHex | kRight | kBase | kPos, /*width = */ 0, "0x2a"}, + {42, kHex | kRight | kBase | kPos, /*width = */ 6, "__0x2a"}, + {42, kHex | kRight | kUpper, /*width = */ 0, "2A"}, + {42, kHex | kRight | kUpper, /*width = */ 6, "____2A"}, + {42, kHex | kRight | kUpper | kPos, /*width = */ 0, "2A"}, + {42, kHex | kRight | kUpper | kPos, /*width = */ 6, "____2A"}, + {42, kHex | kRight | kUpper | kBase, /*width = */ 0, "0X2A"}, + {42, kHex | kRight | kUpper | kBase, /*width = */ 6, "__0X2A"}, + {42, kHex | kRight | kUpper | kBase | kPos, /*width = */ 0, "0X2A"}, + {42, kHex | kRight | kUpper | kBase | kPos, /*width = */ 6, "__0X2A"}, + {-321, std::ios_base::fmtflags(), /*width = */ 0, "-321"}, + {-321, std::ios_base::fmtflags(), /*width = */ 6, "__-321"}, + {-321, kPos, /*width = */ 0, "-321"}, + {-321, kPos, /*width = */ 6, "__-321"}, + {-321, kBase, /*width = */ 0, "-321"}, + {-321, kBase, /*width = */ 6, "__-321"}, + {-321, kBase | kPos, /*width = */ 0, "-321"}, + {-321, kBase | kPos, /*width = */ 6, "__-321"}, + {-321, kUpper, /*width = */ 0, "-321"}, + {-321, kUpper, /*width = */ 6, "__-321"}, + {-321, kUpper | kPos, /*width = */ 0, "-321"}, + {-321, kUpper | kPos, /*width = */ 6, "__-321"}, + {-321, kUpper | kBase, /*width = */ 0, "-321"}, + {-321, kUpper | kBase, /*width = */ 6, "__-321"}, + {-321, kUpper | kBase | kPos, /*width = */ 0, "-321"}, + {-321, kUpper | kBase | kPos, /*width = */ 6, "__-321"}, + {-321, kLeft, /*width = */ 0, "-321"}, + {-321, kLeft, /*width = */ 6, "-321__"}, + {-321, kLeft | kPos, /*width = */ 0, "-321"}, + {-321, kLeft | kPos, /*width = */ 6, "-321__"}, + {-321, kLeft | kBase, /*width = */ 0, "-321"}, + {-321, kLeft | kBase, /*width = */ 6, "-321__"}, + {-321, kLeft | kBase | kPos, /*width = */ 0, "-321"}, + {-321, kLeft | kBase | kPos, /*width = */ 6, "-321__"}, + {-321, kLeft | kUpper, /*width = */ 0, "-321"}, + {-321, kLeft | kUpper, /*width = */ 6, "-321__"}, + {-321, kLeft | kUpper | kPos, /*width = */ 0, "-321"}, + {-321, kLeft | kUpper | kPos, /*width = */ 6, "-321__"}, + {-321, kLeft | kUpper | kBase, /*width = */ 0, "-321"}, + {-321, kLeft | kUpper | kBase, /*width = */ 6, "-321__"}, + {-321, kLeft | kUpper | kBase | kPos, /*width = */ 0, "-321"}, + {-321, kLeft | kUpper | kBase | kPos, /*width = */ 6, "-321__"}, + {-321, kInt, /*width = */ 0, "-321"}, + {-321, kInt, /*width = */ 6, "-__321"}, + {-321, kInt | kPos, /*width = */ 0, "-321"}, + {-321, kInt | kPos, /*width = */ 6, "-__321"}, + {-321, kInt | kBase, /*width = */ 0, "-321"}, + {-321, kInt | kBase, /*width = */ 6, "-__321"}, + {-321, kInt | kBase | kPos, /*width = */ 0, "-321"}, + {-321, kInt | kBase | kPos, /*width = */ 6, "-__321"}, + {-321, kInt | kUpper, /*width = */ 0, "-321"}, + {-321, kInt | kUpper, /*width = */ 6, "-__321"}, + {-321, kInt | kUpper | kPos, /*width = */ 0, "-321"}, + {-321, kInt | kUpper | kPos, /*width = */ 6, "-__321"}, + {-321, kInt | kUpper | kBase, /*width = */ 0, "-321"}, + {-321, kInt | kUpper | kBase, /*width = */ 6, "-__321"}, + {-321, kInt | kUpper | kBase | kPos, /*width = */ 0, "-321"}, + {-321, kInt | kUpper | kBase | kPos, /*width = */ 6, "-__321"}, + {-321, kRight, /*width = */ 0, "-321"}, + {-321, kRight, /*width = */ 6, "__-321"}, + {-321, kRight | kPos, /*width = */ 0, "-321"}, + {-321, kRight | kPos, /*width = */ 6, "__-321"}, + {-321, kRight | kBase, /*width = */ 0, "-321"}, + {-321, kRight | kBase, /*width = */ 6, "__-321"}, + {-321, kRight | kBase | kPos, /*width = */ 0, "-321"}, + {-321, kRight | kBase | kPos, /*width = */ 6, "__-321"}, + {-321, kRight | kUpper, /*width = */ 0, "-321"}, + {-321, kRight | kUpper, /*width = */ 6, "__-321"}, + {-321, kRight | kUpper | kPos, /*width = */ 0, "-321"}, + {-321, kRight | kUpper | kPos, /*width = */ 6, "__-321"}, + {-321, kRight | kUpper | kBase, /*width = */ 0, "-321"}, + {-321, kRight | kUpper | kBase, /*width = */ 6, "__-321"}, + {-321, kRight | kUpper | kBase | kPos, /*width = */ 0, "-321"}, + {-321, kRight | kUpper | kBase | kPos, /*width = */ 6, "__-321"}, + {-321, kDec, /*width = */ 0, "-321"}, + {-321, kDec, /*width = */ 6, "__-321"}, + {-321, kDec | kPos, /*width = */ 0, "-321"}, + {-321, kDec | kPos, /*width = */ 6, "__-321"}, + {-321, kDec | kBase, /*width = */ 0, "-321"}, + {-321, kDec | kBase, /*width = */ 6, "__-321"}, + {-321, kDec | kBase | kPos, /*width = */ 0, "-321"}, + {-321, kDec | kBase | kPos, /*width = */ 6, "__-321"}, + {-321, kDec | kUpper, /*width = */ 0, "-321"}, + {-321, kDec | kUpper, /*width = */ 6, "__-321"}, + {-321, kDec | kUpper | kPos, /*width = */ 0, "-321"}, + {-321, kDec | kUpper | kPos, /*width = */ 6, "__-321"}, + {-321, kDec | kUpper | kBase, /*width = */ 0, "-321"}, + {-321, kDec | kUpper | kBase, /*width = */ 6, "__-321"}, + {-321, kDec | kUpper | kBase | kPos, /*width = */ 0, "-321"}, + {-321, kDec | kUpper | kBase | kPos, /*width = */ 6, "__-321"}, + {-321, kDec | kLeft, /*width = */ 0, "-321"}, + {-321, kDec | kLeft, /*width = */ 6, "-321__"}, + {-321, kDec | kLeft | kPos, /*width = */ 0, "-321"}, + {-321, kDec | kLeft | kPos, /*width = */ 6, "-321__"}, + {-321, kDec | kLeft | kBase, /*width = */ 0, "-321"}, + {-321, kDec | kLeft | kBase, /*width = */ 6, "-321__"}, + {-321, kDec | kLeft | kBase | kPos, /*width = */ 0, "-321"}, + {-321, kDec | kLeft | kBase | kPos, /*width = */ 6, "-321__"}, + {-321, kDec | kLeft | kUpper, /*width = */ 0, "-321"}, + {-321, kDec | kLeft | kUpper, /*width = */ 6, "-321__"}, + {-321, kDec | kLeft | kUpper | kPos, /*width = */ 0, "-321"}, + {-321, kDec | kLeft | kUpper | kPos, /*width = */ 6, "-321__"}, + {-321, kDec | kLeft | kUpper | kBase, /*width = */ 0, "-321"}, + {-321, kDec | kLeft | kUpper | kBase, /*width = */ 6, "-321__"}, + {-321, kDec | kLeft | kUpper | kBase | kPos, /*width = */ 0, "-321"}, + {-321, kDec | kLeft | kUpper | kBase | kPos, /*width = */ 6, "-321__"}, + {-321, kDec | kInt, /*width = */ 0, "-321"}, + {-321, kDec | kInt, /*width = */ 6, "-__321"}, + {-321, kDec | kInt | kPos, /*width = */ 0, "-321"}, + {-321, kDec | kInt | kPos, /*width = */ 6, "-__321"}, + {-321, kDec | kInt | kBase, /*width = */ 0, "-321"}, + {-321, kDec | kInt | kBase, /*width = */ 6, "-__321"}, + {-321, kDec | kInt | kBase | kPos, /*width = */ 0, "-321"}, + {-321, kDec | kInt | kBase | kPos, /*width = */ 6, "-__321"}, + {-321, kDec | kInt | kUpper, /*width = */ 0, "-321"}, + {-321, kDec | kInt | kUpper, /*width = */ 6, "-__321"}, + {-321, kDec | kInt | kUpper | kPos, /*width = */ 0, "-321"}, + {-321, kDec | kInt | kUpper | kPos, /*width = */ 6, "-__321"}, + {-321, kDec | kInt | kUpper | kBase, /*width = */ 0, "-321"}, + {-321, kDec | kInt | kUpper | kBase, /*width = */ 6, "-__321"}, + {-321, kDec | kInt | kUpper | kBase | kPos, /*width = */ 0, "-321"}, + {-321, kDec | kInt | kUpper | kBase | kPos, /*width = */ 6, "-__321"}, + {-321, kDec | kRight, /*width = */ 0, "-321"}, + {-321, kDec | kRight, /*width = */ 6, "__-321"}, + {-321, kDec | kRight | kPos, /*width = */ 0, "-321"}, + {-321, kDec | kRight | kPos, /*width = */ 6, "__-321"}, + {-321, kDec | kRight | kBase, /*width = */ 0, "-321"}, + {-321, kDec | kRight | kBase, /*width = */ 6, "__-321"}, + {-321, kDec | kRight | kBase | kPos, /*width = */ 0, "-321"}, + {-321, kDec | kRight | kBase | kPos, /*width = */ 6, "__-321"}, + {-321, kDec | kRight | kUpper, /*width = */ 0, "-321"}, + {-321, kDec | kRight | kUpper, /*width = */ 6, "__-321"}, + {-321, kDec | kRight | kUpper | kPos, /*width = */ 0, "-321"}, + {-321, kDec | kRight | kUpper | kPos, /*width = */ 6, "__-321"}, + {-321, kDec | kRight | kUpper | kBase, /*width = */ 0, "-321"}, + {-321, kDec | kRight | kUpper | kBase, /*width = */ 6, "__-321"}, + {-321, kDec | kRight | kUpper | kBase | kPos, /*width = */ 0, "-321"}, + {-321, kDec | kRight | kUpper | kBase | kPos, /*width = */ 6, "__-321"}}; +} + std::vector GetUint128FormatCases() { return { {0, std::ios_base::fmtflags(), /*width = */ 0, "0"}, diff --git a/absl/numeric/int128_test.cc b/absl/numeric/int128_test.cc index 5e1b5ec352b..bc86c714acc 100644 --- a/absl/numeric/int128_test.cc +++ b/absl/numeric/int128_test.cc @@ -479,4 +479,747 @@ TEST(Uint128, Hash) { })); } + +TEST(Int128Uint128, ConversionTest) { + absl::int128 nonnegative_signed_values[] = { + 0, + 1, + 0xffeeddccbbaa9988, + absl::MakeInt128(0x7766554433221100, 0), + absl::MakeInt128(0x1234567890abcdef, 0xfedcba0987654321), + absl::Int128Max()}; + for (absl::int128 value : nonnegative_signed_values) { + EXPECT_EQ(value, absl::int128(absl::uint128(value))); + + absl::uint128 assigned_value; + assigned_value = value; + EXPECT_EQ(value, absl::int128(assigned_value)); + } + + absl::int128 negative_values[] = { + -1, -0x1234567890abcdef, + absl::MakeInt128(-0x5544332211ffeedd, 0), + -absl::MakeInt128(0x76543210fedcba98, 0xabcdef0123456789)}; + for (absl::int128 value : negative_values) { + EXPECT_EQ(absl::uint128(-value), -absl::uint128(value)); + + absl::uint128 assigned_value; + assigned_value = value; + EXPECT_EQ(absl::uint128(-value), -assigned_value); + } +} + +template +class Int128IntegerTraitsTest : public ::testing::Test {}; + +TYPED_TEST_SUITE(Int128IntegerTraitsTest, IntegerTypes); + +TYPED_TEST(Int128IntegerTraitsTest, ConstructAssignTest) { + static_assert(std::is_constructible::value, + "absl::int128 must be constructible from TypeParam"); + static_assert(std::is_assignable::value, + "absl::int128 must be assignable from TypeParam"); + static_assert(!std::is_assignable::value, + "TypeParam must not be assignable from absl::int128"); +} + +template +class Int128FloatTraitsTest : public ::testing::Test {}; + +TYPED_TEST_SUITE(Int128FloatTraitsTest, FloatingPointTypes); + +TYPED_TEST(Int128FloatTraitsTest, ConstructAssignTest) { + static_assert(std::is_constructible::value, + "absl::int128 must be constructible from TypeParam"); + static_assert(!std::is_assignable::value, + "absl::int128 must not be assignable from TypeParam"); + static_assert(!std::is_assignable::value, + "TypeParam must not be assignable from absl::int128"); +} + +#ifdef ABSL_HAVE_INTRINSIC_INT128 +// These type traits done separately as TYPED_TEST requires typeinfo, and not +// all platforms have this for __int128 even though they define the type. +TEST(Int128, IntrinsicTypeTraitsTest) { + static_assert(std::is_constructible::value, + "absl::int128 must be constructible from __int128"); + static_assert(std::is_assignable::value, + "absl::int128 must be assignable from __int128"); + static_assert(!std::is_assignable<__int128&, absl::int128>::value, + "__int128 must not be assignable from absl::int128"); + + static_assert(std::is_constructible::value, + "absl::int128 must be constructible from unsigned __int128"); + static_assert(!std::is_assignable::value, + "absl::int128 must be assignable from unsigned __int128"); + static_assert(!std::is_assignable::value, + "unsigned __int128 must not be assignable from absl::int128"); +} +#endif // ABSL_HAVE_INTRINSIC_INT128 + +TEST(Int128, TrivialTraitsTest) { + static_assert(absl::is_trivially_default_constructible::value, + ""); + static_assert(absl::is_trivially_copy_constructible::value, ""); + static_assert(absl::is_trivially_copy_assignable::value, ""); + static_assert(std::is_trivially_destructible::value, ""); +} + +TEST(Int128, BoolConversionTest) { + EXPECT_FALSE(absl::int128(0)); + for (int i = 0; i < 64; ++i) { + EXPECT_TRUE(absl::MakeInt128(0, uint64_t{1} << i)); + } + for (int i = 0; i < 63; ++i) { + EXPECT_TRUE(absl::MakeInt128(int64_t{1} << i, 0)); + } + EXPECT_TRUE(absl::Int128Min()); + + EXPECT_EQ(absl::int128(1), absl::int128(true)); + EXPECT_EQ(absl::int128(0), absl::int128(false)); +} + +template +class Int128IntegerConversionTest : public ::testing::Test {}; + +TYPED_TEST_SUITE(Int128IntegerConversionTest, IntegerTypes); + +TYPED_TEST(Int128IntegerConversionTest, RoundTripTest) { + EXPECT_EQ(TypeParam{0}, static_cast(absl::int128(0))); + EXPECT_EQ(std::numeric_limits::min(), + static_cast( + absl::int128(std::numeric_limits::min()))); + EXPECT_EQ(std::numeric_limits::max(), + static_cast( + absl::int128(std::numeric_limits::max()))); +} + +template +class Int128FloatConversionTest : public ::testing::Test {}; + +TYPED_TEST_SUITE(Int128FloatConversionTest, FloatingPointTypes); + +TYPED_TEST(Int128FloatConversionTest, ConstructAndCastTest) { + // Conversions where the floating point values should be exactly the same. + // 0x9f5b is a randomly chosen small value. + for (int i = 0; i < 110; ++i) { // 110 = 126 - #bits in 0x9f5b + SCOPED_TRACE(::testing::Message() << "i = " << i); + + TypeParam float_value = std::ldexp(static_cast(0x9f5b), i); + absl::int128 int_value = absl::int128(0x9f5b) << i; + + EXPECT_EQ(float_value, static_cast(int_value)); + EXPECT_EQ(-float_value, static_cast(-int_value)); + EXPECT_EQ(int_value, absl::int128(float_value)); + EXPECT_EQ(-int_value, absl::int128(-float_value)); + } + + // Round trip conversions with a small sample of randomly generated uint64_t + // values (less than int64_t max so that value * 2^64 fits into int128). + uint64_t values[] = {0x6d4492c24fb86199, 0x26ead65e4cb359b5, + 0x2c43407433ba3fd1, 0x3b574ec668df6b55, + 0x1c750e55a29f4f0f}; + for (uint64_t value : values) { + for (int i = 0; i <= 64; ++i) { + SCOPED_TRACE(::testing::Message() + << "value = " << value << "; i = " << i); + + TypeParam fvalue = std::ldexp(static_cast(value), i); + EXPECT_DOUBLE_EQ(fvalue, static_cast(absl::int128(fvalue))); + EXPECT_DOUBLE_EQ(-fvalue, static_cast(-absl::int128(fvalue))); + EXPECT_DOUBLE_EQ(-fvalue, static_cast(absl::int128(-fvalue))); + EXPECT_DOUBLE_EQ(fvalue, static_cast(-absl::int128(-fvalue))); + } + } + + // Round trip conversions with a small sample of random large positive values. + absl::int128 large_values[] = { + absl::MakeInt128(0x5b0640d96c7b3d9f, 0xb7a7189e51d18622), + absl::MakeInt128(0x34bed042c6f65270, 0x73b236570669a089), + absl::MakeInt128(0x43deba9e6da12724, 0xf7f0f83da686797d), + absl::MakeInt128(0x71e8d383be4e5589, 0x75c3f96fb00752b6)}; + for (absl::int128 value : large_values) { + // Make value have as many significant bits as can be represented by + // the mantissa, also making sure the highest and lowest bit in the range + // are set. + value >>= (127 - std::numeric_limits::digits); + value |= absl::int128(1) << (std::numeric_limits::digits - 1); + value |= 1; + for (int i = 0; i < 127 - std::numeric_limits::digits; ++i) { + absl::int128 int_value = value << i; + EXPECT_EQ(int_value, + static_cast(static_cast(int_value))); + EXPECT_EQ(-int_value, + static_cast(static_cast(-int_value))); + } + } + + // Small sample of checks that rounding is toward zero + EXPECT_EQ(0, absl::int128(TypeParam(0.1))); + EXPECT_EQ(17, absl::int128(TypeParam(17.8))); + EXPECT_EQ(0, absl::int128(TypeParam(-0.8))); + EXPECT_EQ(-53, absl::int128(TypeParam(-53.1))); + EXPECT_EQ(0, absl::int128(TypeParam(0.5))); + EXPECT_EQ(0, absl::int128(TypeParam(-0.5))); + TypeParam just_lt_one = std::nexttoward(TypeParam(1), TypeParam(0)); + EXPECT_EQ(0, absl::int128(just_lt_one)); + TypeParam just_gt_minus_one = std::nexttoward(TypeParam(-1), TypeParam(0)); + EXPECT_EQ(0, absl::int128(just_gt_minus_one)); + + // Check limits + EXPECT_DOUBLE_EQ(std::ldexp(static_cast(1), 127), + static_cast(absl::Int128Max())); + EXPECT_DOUBLE_EQ(-std::ldexp(static_cast(1), 127), + static_cast(absl::Int128Min())); +} + +TEST(Int128, FactoryTest) { + EXPECT_EQ(absl::int128(-1), absl::MakeInt128(-1, -1)); + EXPECT_EQ(absl::int128(-31), absl::MakeInt128(-1, -31)); + EXPECT_EQ(absl::int128(std::numeric_limits::min()), + absl::MakeInt128(-1, std::numeric_limits::min())); + EXPECT_EQ(absl::int128(0), absl::MakeInt128(0, 0)); + EXPECT_EQ(absl::int128(1), absl::MakeInt128(0, 1)); + EXPECT_EQ(absl::int128(std::numeric_limits::max()), + absl::MakeInt128(0, std::numeric_limits::max())); +} + +TEST(Int128, HighLowTest) { + struct HighLowPair { + int64_t high; + uint64_t low; + }; + HighLowPair values[]{{0, 0}, {0, 1}, {1, 0}, {123, 456}, {-654, 321}}; + for (const HighLowPair& pair : values) { + absl::int128 value = absl::MakeInt128(pair.high, pair.low); + EXPECT_EQ(pair.low, absl::Int128Low64(value)); + EXPECT_EQ(pair.high, absl::Int128High64(value)); + } +} + +TEST(Int128, LimitsTest) { + EXPECT_EQ(absl::MakeInt128(0x7fffffffffffffff, 0xffffffffffffffff), + absl::Int128Max()); + EXPECT_EQ(absl::Int128Max(), ~absl::Int128Min()); +} + +#if defined(ABSL_HAVE_INTRINSIC_INT128) +TEST(Int128, IntrinsicConversionTest) { + __int128 intrinsic = + (static_cast<__int128>(0x3a5b76c209de76f6) << 64) + 0x1f25e1d63a2b46c5; + absl::int128 custom = + absl::MakeInt128(0x3a5b76c209de76f6, 0x1f25e1d63a2b46c5); + + EXPECT_EQ(custom, absl::int128(intrinsic)); + EXPECT_EQ(intrinsic, static_cast<__int128>(custom)); +} +#endif // ABSL_HAVE_INTRINSIC_INT128 + +TEST(Int128, ConstexprTest) { + constexpr absl::int128 zero = absl::int128(); + constexpr absl::int128 one = 1; + constexpr absl::int128 minus_two = -2; + constexpr absl::int128 min = absl::Int128Min(); + constexpr absl::int128 max = absl::Int128Max(); + EXPECT_EQ(zero, absl::int128(0)); + EXPECT_EQ(one, absl::int128(1)); + EXPECT_EQ(minus_two, absl::MakeInt128(-1, -2)); + EXPECT_GT(max, one); + EXPECT_LT(min, minus_two); +} + +TEST(Int128, ComparisonTest) { + struct TestCase { + absl::int128 smaller; + absl::int128 larger; + }; + TestCase cases[] = { + {absl::int128(0), absl::int128(123)}, + {absl::MakeInt128(-12, 34), absl::MakeInt128(12, 34)}, + {absl::MakeInt128(1, 1000), absl::MakeInt128(1000, 1)}, + {absl::MakeInt128(-1000, 1000), absl::MakeInt128(-1, 1)}, + }; + for (const TestCase& pair : cases) { + SCOPED_TRACE(::testing::Message() << "pair.smaller = " << pair.smaller + << "; pair.larger = " << pair.larger); + + EXPECT_TRUE(pair.smaller == pair.smaller); // NOLINT(readability/check) + EXPECT_TRUE(pair.larger == pair.larger); // NOLINT(readability/check) + EXPECT_FALSE(pair.smaller == pair.larger); // NOLINT(readability/check) + + EXPECT_TRUE(pair.smaller != pair.larger); // NOLINT(readability/check) + EXPECT_FALSE(pair.smaller != pair.smaller); // NOLINT(readability/check) + EXPECT_FALSE(pair.larger != pair.larger); // NOLINT(readability/check) + + EXPECT_TRUE(pair.smaller < pair.larger); // NOLINT(readability/check) + EXPECT_FALSE(pair.larger < pair.smaller); // NOLINT(readability/check) + + EXPECT_TRUE(pair.larger > pair.smaller); // NOLINT(readability/check) + EXPECT_FALSE(pair.smaller > pair.larger); // NOLINT(readability/check) + + EXPECT_TRUE(pair.smaller <= pair.larger); // NOLINT(readability/check) + EXPECT_FALSE(pair.larger <= pair.smaller); // NOLINT(readability/check) + EXPECT_TRUE(pair.smaller <= pair.smaller); // NOLINT(readability/check) + EXPECT_TRUE(pair.larger <= pair.larger); // NOLINT(readability/check) + + EXPECT_TRUE(pair.larger >= pair.smaller); // NOLINT(readability/check) + EXPECT_FALSE(pair.smaller >= pair.larger); // NOLINT(readability/check) + EXPECT_TRUE(pair.smaller >= pair.smaller); // NOLINT(readability/check) + EXPECT_TRUE(pair.larger >= pair.larger); // NOLINT(readability/check) + } +} + +TEST(Int128, UnaryNegationTest) { + int64_t values64[] = {0, 1, 12345, 0x4000000000000000, + std::numeric_limits::max()}; + for (int64_t value : values64) { + SCOPED_TRACE(::testing::Message() << "value = " << value); + + EXPECT_EQ(absl::int128(-value), -absl::int128(value)); + EXPECT_EQ(absl::int128(value), -absl::int128(-value)); + EXPECT_EQ(absl::MakeInt128(-value, 0), -absl::MakeInt128(value, 0)); + EXPECT_EQ(absl::MakeInt128(value, 0), -absl::MakeInt128(-value, 0)); + } +} + +TEST(Int128, LogicalNotTest) { + EXPECT_TRUE(!absl::int128(0)); + for (int i = 0; i < 64; ++i) { + EXPECT_FALSE(!absl::MakeInt128(0, uint64_t{1} << i)); + } + for (int i = 0; i < 63; ++i) { + EXPECT_FALSE(!absl::MakeInt128(int64_t{1} << i, 0)); + } +} + +TEST(Int128, AdditionSubtractionTest) { + // 64 bit pairs that will not cause overflow / underflow. These test negative + // carry; positive carry must be checked separately. + std::pair cases[]{ + {0, 0}, // 0, 0 + {0, 2945781290834}, // 0, + + {1908357619234, 0}, // +, 0 + {0, -1204895918245}, // 0, - + {-2957928523560, 0}, // -, 0 + {89023982312461, 98346012567134}, // +, + + {-63454234568239, -23456235230773}, // -, - + {98263457263502, -21428561935925}, // +, - + {-88235237438467, 15923659234573}, // -, + + }; + for (const auto& pair : cases) { + SCOPED_TRACE(::testing::Message() + << "pair = {" << pair.first << ", " << pair.second << '}'); + + EXPECT_EQ(absl::int128(pair.first + pair.second), + absl::int128(pair.first) + absl::int128(pair.second)); + EXPECT_EQ(absl::int128(pair.second + pair.first), + absl::int128(pair.second) += absl::int128(pair.first)); + + EXPECT_EQ(absl::int128(pair.first - pair.second), + absl::int128(pair.first) - absl::int128(pair.second)); + EXPECT_EQ(absl::int128(pair.second - pair.first), + absl::int128(pair.second) -= absl::int128(pair.first)); + + EXPECT_EQ( + absl::MakeInt128(pair.second + pair.first, 0), + absl::MakeInt128(pair.second, 0) + absl::MakeInt128(pair.first, 0)); + EXPECT_EQ( + absl::MakeInt128(pair.first + pair.second, 0), + absl::MakeInt128(pair.first, 0) += absl::MakeInt128(pair.second, 0)); + + EXPECT_EQ( + absl::MakeInt128(pair.second - pair.first, 0), + absl::MakeInt128(pair.second, 0) - absl::MakeInt128(pair.first, 0)); + EXPECT_EQ( + absl::MakeInt128(pair.first - pair.second, 0), + absl::MakeInt128(pair.first, 0) -= absl::MakeInt128(pair.second, 0)); + } + + // check positive carry + EXPECT_EQ(absl::MakeInt128(31, 0), + absl::MakeInt128(20, 1) + + absl::MakeInt128(10, std::numeric_limits::max())); +} + +TEST(Int128, IncrementDecrementTest) { + absl::int128 value = 0; + EXPECT_EQ(0, value++); + EXPECT_EQ(1, value); + EXPECT_EQ(1, value--); + EXPECT_EQ(0, value); + EXPECT_EQ(-1, --value); + EXPECT_EQ(-1, value); + EXPECT_EQ(0, ++value); + EXPECT_EQ(0, value); +} + +TEST(Int128, MultiplicationTest) { + // 1 bit x 1 bit, and negative combinations + for (int i = 0; i < 64; ++i) { + for (int j = 0; j < 127 - i; ++j) { + SCOPED_TRACE(::testing::Message() << "i = " << i << "; j = " << j); + absl::int128 a = absl::int128(1) << i; + absl::int128 b = absl::int128(1) << j; + absl::int128 c = absl::int128(1) << (i + j); + + EXPECT_EQ(c, a * b); + EXPECT_EQ(-c, -a * b); + EXPECT_EQ(-c, a * -b); + EXPECT_EQ(c, -a * -b); + + EXPECT_EQ(c, absl::int128(a) *= b); + EXPECT_EQ(-c, absl::int128(-a) *= b); + EXPECT_EQ(-c, absl::int128(a) *= -b); + EXPECT_EQ(c, absl::int128(-a) *= -b); + } + } + + // Pairs of random values that will not overflow signed 64-bit multiplication + std::pair small_values[] = { + {0x5e61, 0xf29f79ca14b4}, // +, + + {0x3e033b, -0x612c0ee549}, // +, - + {-0x052ce7e8, 0x7c728f0f}, // -, + + {-0x3af7054626, -0xfb1e1d}, // -, - + }; + for (const std::pair& pair : small_values) { + SCOPED_TRACE(::testing::Message() + << "pair = {" << pair.first << ", " << pair.second << '}'); + + EXPECT_EQ(absl::int128(pair.first * pair.second), + absl::int128(pair.first) * absl::int128(pair.second)); + EXPECT_EQ(absl::int128(pair.first * pair.second), + absl::int128(pair.first) *= absl::int128(pair.second)); + + EXPECT_EQ(absl::MakeInt128(pair.first * pair.second, 0), + absl::MakeInt128(pair.first, 0) * absl::int128(pair.second)); + EXPECT_EQ(absl::MakeInt128(pair.first * pair.second, 0), + absl::MakeInt128(pair.first, 0) *= absl::int128(pair.second)); + } + + // Pairs of positive random values that will not overflow 64-bit + // multiplication and can be left shifted by 32 without overflow + std::pair small_values2[] = { + {0x1bb0a110, 0x31487671}, + {0x4792784e, 0x28add7d7}, + {0x7b66553a, 0x11dff8ef}, + }; + for (const std::pair& pair : small_values2) { + SCOPED_TRACE(::testing::Message() + << "pair = {" << pair.first << ", " << pair.second << '}'); + + absl::int128 a = absl::int128(pair.first << 32); + absl::int128 b = absl::int128(pair.second << 32); + absl::int128 c = absl::MakeInt128(pair.first * pair.second, 0); + + EXPECT_EQ(c, a * b); + EXPECT_EQ(-c, -a * b); + EXPECT_EQ(-c, a * -b); + EXPECT_EQ(c, -a * -b); + + EXPECT_EQ(c, absl::int128(a) *= b); + EXPECT_EQ(-c, absl::int128(-a) *= b); + EXPECT_EQ(-c, absl::int128(a) *= -b); + EXPECT_EQ(c, absl::int128(-a) *= -b); + } + + // check 0, 1, and -1 behavior with large values + absl::int128 large_values[] = { + {absl::MakeInt128(0xd66f061af02d0408, 0x727d2846cb475b53)}, + {absl::MakeInt128(0x27b8d5ed6104452d, 0x03f8a33b0ee1df4f)}, + {-absl::MakeInt128(0x621b6626b9e8d042, 0x27311ac99df00938)}, + {-absl::MakeInt128(0x34e0656f1e95fb60, 0x4281cfd731257a47)}, + }; + for (absl::int128 value : large_values) { + EXPECT_EQ(0, 0 * value); + EXPECT_EQ(0, value * 0); + EXPECT_EQ(0, absl::int128(0) *= value); + EXPECT_EQ(0, value *= 0); + + EXPECT_EQ(value, 1 * value); + EXPECT_EQ(value, value * 1); + EXPECT_EQ(value, absl::int128(1) *= value); + EXPECT_EQ(value, value *= 1); + + EXPECT_EQ(-value, -1 * value); + EXPECT_EQ(-value, value * -1); + EXPECT_EQ(-value, absl::int128(-1) *= value); + EXPECT_EQ(-value, value *= -1); + } + + // Manually calculated random large value cases + EXPECT_EQ(absl::MakeInt128(0xcd0efd3442219bb, 0xde47c05bcd9df6e1), + absl::MakeInt128(0x7c6448, 0x3bc4285c47a9d253) * 0x1a6037537b); + EXPECT_EQ(-absl::MakeInt128(0x1f8f149850b1e5e6, 0x1e50d6b52d272c3e), + -absl::MakeInt128(0x23, 0x2e68a513ca1b8859) * 0xe5a434cd14866e); + EXPECT_EQ(-absl::MakeInt128(0x55cae732029d1fce, 0xca6474b6423263e4), + 0xa9b98a8ddf66bc * -absl::MakeInt128(0x81, 0x672e58231e2469d7)); + EXPECT_EQ(absl::MakeInt128(0x19c8b7620b507dc4, 0xfec042b71a5f29a4), + -0x3e39341147 * -absl::MakeInt128(0x6a14b2, 0x5ed34cca42327b3c)); + + EXPECT_EQ(absl::MakeInt128(0xcd0efd3442219bb, 0xde47c05bcd9df6e1), + absl::MakeInt128(0x7c6448, 0x3bc4285c47a9d253) *= 0x1a6037537b); + EXPECT_EQ(-absl::MakeInt128(0x1f8f149850b1e5e6, 0x1e50d6b52d272c3e), + -absl::MakeInt128(0x23, 0x2e68a513ca1b8859) *= 0xe5a434cd14866e); + EXPECT_EQ(-absl::MakeInt128(0x55cae732029d1fce, 0xca6474b6423263e4), + absl::int128(0xa9b98a8ddf66bc) *= + -absl::MakeInt128(0x81, 0x672e58231e2469d7)); + EXPECT_EQ(absl::MakeInt128(0x19c8b7620b507dc4, 0xfec042b71a5f29a4), + absl::int128(-0x3e39341147) *= + -absl::MakeInt128(0x6a14b2, 0x5ed34cca42327b3c)); +} + +TEST(Int128, DivisionAndModuloTest) { + // Check against 64 bit division and modulo operators with a sample of + // randomly generated pairs. + std::pair small_pairs[] = { + {0x15f2a64138, 0x67da05}, {0x5e56d194af43045f, 0xcf1543fb99}, + {0x15e61ed052036a, -0xc8e6}, {0x88125a341e85, -0xd23fb77683}, + {-0xc06e20, 0x5a}, {-0x4f100219aea3e85d, 0xdcc56cb4efe993}, + {-0x168d629105, -0xa7}, {-0x7b44e92f03ab2375, -0x6516}, + }; + for (const std::pair& pair : small_pairs) { + SCOPED_TRACE(::testing::Message() + << "pair = {" << pair.first << ", " << pair.second << '}'); + + absl::int128 dividend = pair.first; + absl::int128 divisor = pair.second; + int64_t quotient = pair.first / pair.second; + int64_t remainder = pair.first % pair.second; + + EXPECT_EQ(quotient, dividend / divisor); + EXPECT_EQ(quotient, absl::int128(dividend) /= divisor); + EXPECT_EQ(remainder, dividend % divisor); + EXPECT_EQ(remainder, absl::int128(dividend) %= divisor); + } + + // Test behavior with 0, 1, and -1 with a sample of randomly generated large + // values. + absl::int128 values[] = { + absl::MakeInt128(0x63d26ee688a962b2, 0x9e1411abda5c1d70), + absl::MakeInt128(0x152f385159d6f986, 0xbf8d48ef63da395d), + -absl::MakeInt128(0x3098d7567030038c, 0x14e7a8a098dc2164), + -absl::MakeInt128(0x49a037aca35c809f, 0xa6a87525480ef330), + }; + for (absl::int128 value : values) { + SCOPED_TRACE(::testing::Message() << "value = " << value); + + EXPECT_EQ(0, 0 / value); + EXPECT_EQ(0, absl::int128(0) /= value); + EXPECT_EQ(0, 0 % value); + EXPECT_EQ(0, absl::int128(0) %= value); + + EXPECT_EQ(value, value / 1); + EXPECT_EQ(value, absl::int128(value) /= 1); + EXPECT_EQ(0, value % 1); + EXPECT_EQ(0, absl::int128(value) %= 1); + + EXPECT_EQ(-value, value / -1); + EXPECT_EQ(-value, absl::int128(value) /= -1); + EXPECT_EQ(0, value % -1); + EXPECT_EQ(0, absl::int128(value) %= -1); + } + + // Min and max values + EXPECT_EQ(0, absl::Int128Max() / absl::Int128Min()); + EXPECT_EQ(absl::Int128Max(), absl::Int128Max() % absl::Int128Min()); + EXPECT_EQ(-1, absl::Int128Min() / absl::Int128Max()); + EXPECT_EQ(-1, absl::Int128Min() % absl::Int128Max()); + + // Power of two division and modulo of random large dividends + absl::int128 positive_values[] = { + absl::MakeInt128(0x21e1a1cc69574620, 0xe7ac447fab2fc869), + absl::MakeInt128(0x32c2ff3ab89e66e8, 0x03379a613fd1ce74), + absl::MakeInt128(0x6f32ca786184dcaf, 0x046f9c9ecb3a9ce1), + absl::MakeInt128(0x1aeb469dd990e0ee, 0xda2740f243cd37eb), + }; + for (absl::int128 value : positive_values) { + for (int i = 0; i < 127; ++i) { + SCOPED_TRACE(::testing::Message() + << "value = " << value << "; i = " << i); + absl::int128 power_of_two = absl::int128(1) << i; + + EXPECT_EQ(value >> i, value / power_of_two); + EXPECT_EQ(value >> i, absl::int128(value) /= power_of_two); + EXPECT_EQ(value & (power_of_two - 1), value % power_of_two); + EXPECT_EQ(value & (power_of_two - 1), + absl::int128(value) %= power_of_two); + } + } + + // Manually calculated cases with random large dividends + struct DivisionModCase { + absl::int128 dividend; + absl::int128 divisor; + absl::int128 quotient; + absl::int128 remainder; + }; + DivisionModCase manual_cases[] = { + {absl::MakeInt128(0x6ada48d489007966, 0x3c9c5c98150d5d69), + absl::MakeInt128(0x8bc308fb, 0x8cb9cc9a3b803344), 0xc3b87e08, + absl::MakeInt128(0x1b7db5e1, 0xd9eca34b7af04b49)}, + {absl::MakeInt128(0xd6946511b5b, 0x4886c5c96546bf5f), + -absl::MakeInt128(0x263b, 0xfd516279efcfe2dc), -0x59cbabf0, + absl::MakeInt128(0x622, 0xf462909155651d1f)}, + {-absl::MakeInt128(0x33db734f9e8d1399, 0x8447ac92482bca4d), 0x37495078240, + -absl::MakeInt128(0xf01f1, 0xbc0368bf9a77eae8), -0x21a508f404d}, + {-absl::MakeInt128(0x13f837b409a07e7d, 0x7fc8e248a7d73560), -0x1b9f, + absl::MakeInt128(0xb9157556d724, 0xb14f635714d7563e), -0x1ade}, + }; + for (const DivisionModCase test_case : manual_cases) { + EXPECT_EQ(test_case.quotient, test_case.dividend / test_case.divisor); + EXPECT_EQ(test_case.quotient, + absl::int128(test_case.dividend) /= test_case.divisor); + EXPECT_EQ(test_case.remainder, test_case.dividend % test_case.divisor); + EXPECT_EQ(test_case.remainder, + absl::int128(test_case.dividend) %= test_case.divisor); + } +} + +TEST(Int128, BitwiseLogicTest) { + EXPECT_EQ(absl::int128(-1), ~absl::int128(0)); + + absl::int128 values[]{ + 0, -1, 0xde400bee05c3ff6b, absl::MakeInt128(0x7f32178dd81d634a, 0), + absl::MakeInt128(0xaf539057055613a9, 0x7d104d7d946c2e4d)}; + for (absl::int128 value : values) { + EXPECT_EQ(value, ~~value); + + EXPECT_EQ(value, value | value); + EXPECT_EQ(value, value & value); + EXPECT_EQ(0, value ^ value); + + EXPECT_EQ(value, absl::int128(value) |= value); + EXPECT_EQ(value, absl::int128(value) &= value); + EXPECT_EQ(0, absl::int128(value) ^= value); + + EXPECT_EQ(value, value | 0); + EXPECT_EQ(0, value & 0); + EXPECT_EQ(value, value ^ 0); + + EXPECT_EQ(absl::int128(-1), value | absl::int128(-1)); + EXPECT_EQ(value, value & absl::int128(-1)); + EXPECT_EQ(~value, value ^ absl::int128(-1)); + } + + // small sample of randomly generated int64_t's + std::pair pairs64[]{ + {0x7f86797f5e991af4, 0x1ee30494fb007c97}, + {0x0b278282bacf01af, 0x58780e0a57a49e86}, + {0x059f266ccb93a666, 0x3d5b731bae9286f5}, + {0x63c0c4820f12108c, 0x58166713c12e1c3a}, + {0x381488bb2ed2a66e, 0x2220a3eb76a3698c}, + {0x2a0a0dfb81e06f21, 0x4b60585927f5523c}, + {0x555b1c3a03698537, 0x25478cd19d8e53cb}, + {0x4750f6f27d779225, 0x16397553c6ff05fc}, + }; + for (const std::pair& pair : pairs64) { + SCOPED_TRACE(::testing::Message() + << "pair = {" << pair.first << ", " << pair.second << '}'); + + EXPECT_EQ(absl::MakeInt128(~pair.first, ~pair.second), + ~absl::MakeInt128(pair.first, pair.second)); + + EXPECT_EQ(absl::int128(pair.first & pair.second), + absl::int128(pair.first) & absl::int128(pair.second)); + EXPECT_EQ(absl::int128(pair.first | pair.second), + absl::int128(pair.first) | absl::int128(pair.second)); + EXPECT_EQ(absl::int128(pair.first ^ pair.second), + absl::int128(pair.first) ^ absl::int128(pair.second)); + + EXPECT_EQ(absl::int128(pair.first & pair.second), + absl::int128(pair.first) &= absl::int128(pair.second)); + EXPECT_EQ(absl::int128(pair.first | pair.second), + absl::int128(pair.first) |= absl::int128(pair.second)); + EXPECT_EQ(absl::int128(pair.first ^ pair.second), + absl::int128(pair.first) ^= absl::int128(pair.second)); + + EXPECT_EQ( + absl::MakeInt128(pair.first & pair.second, 0), + absl::MakeInt128(pair.first, 0) & absl::MakeInt128(pair.second, 0)); + EXPECT_EQ( + absl::MakeInt128(pair.first | pair.second, 0), + absl::MakeInt128(pair.first, 0) | absl::MakeInt128(pair.second, 0)); + EXPECT_EQ( + absl::MakeInt128(pair.first ^ pair.second, 0), + absl::MakeInt128(pair.first, 0) ^ absl::MakeInt128(pair.second, 0)); + + EXPECT_EQ( + absl::MakeInt128(pair.first & pair.second, 0), + absl::MakeInt128(pair.first, 0) &= absl::MakeInt128(pair.second, 0)); + EXPECT_EQ( + absl::MakeInt128(pair.first | pair.second, 0), + absl::MakeInt128(pair.first, 0) |= absl::MakeInt128(pair.second, 0)); + EXPECT_EQ( + absl::MakeInt128(pair.first ^ pair.second, 0), + absl::MakeInt128(pair.first, 0) ^= absl::MakeInt128(pair.second, 0)); + } +} + +TEST(Int128, BitwiseShiftTest) { + for (int i = 0; i < 64; ++i) { + for (int j = 0; j <= i; ++j) { + // Left shift from j-th bit to i-th bit. + SCOPED_TRACE(::testing::Message() << "i = " << i << "; j = " << j); + EXPECT_EQ(uint64_t{1} << i, absl::int128(uint64_t{1} << j) << (i - j)); + EXPECT_EQ(uint64_t{1} << i, absl::int128(uint64_t{1} << j) <<= (i - j)); + } + } + for (int i = 0; i < 63; ++i) { + for (int j = 0; j < 64; ++j) { + // Left shift from j-th bit to (i + 64)-th bit. + SCOPED_TRACE(::testing::Message() << "i = " << i << "; j = " << j); + EXPECT_EQ(absl::MakeInt128(uint64_t{1} << i, 0), + absl::int128(uint64_t{1} << j) << (i + 64 - j)); + EXPECT_EQ(absl::MakeInt128(uint64_t{1} << i, 0), + absl::int128(uint64_t{1} << j) <<= (i + 64 - j)); + } + for (int j = 0; j <= i; ++j) { + // Left shift from (j + 64)-th bit to (i + 64)-th bit. + SCOPED_TRACE(::testing::Message() << "i = " << i << "; j = " << j); + EXPECT_EQ(absl::MakeInt128(uint64_t{1} << i, 0), + absl::MakeInt128(uint64_t{1} << j, 0) << (i - j)); + EXPECT_EQ(absl::MakeInt128(uint64_t{1} << i, 0), + absl::MakeInt128(uint64_t{1} << j, 0) <<= (i - j)); + } + } + + for (int i = 0; i < 64; ++i) { + for (int j = i; j < 64; ++j) { + // Right shift from j-th bit to i-th bit. + SCOPED_TRACE(::testing::Message() << "i = " << i << "; j = " << j); + EXPECT_EQ(uint64_t{1} << i, absl::int128(uint64_t{1} << j) >> (j - i)); + EXPECT_EQ(uint64_t{1} << i, absl::int128(uint64_t{1} << j) >>= (j - i)); + } + for (int j = 0; j < 63; ++j) { + // Right shift from (j + 64)-th bit to i-th bit. + SCOPED_TRACE(::testing::Message() << "i = " << i << "; j = " << j); + EXPECT_EQ(uint64_t{1} << i, + absl::MakeInt128(uint64_t{1} << j, 0) >> (j + 64 - i)); + EXPECT_EQ(uint64_t{1} << i, + absl::MakeInt128(uint64_t{1} << j, 0) >>= (j + 64 - i)); + } + } + for (int i = 0; i < 63; ++i) { + for (int j = i; j < 63; ++j) { + // Right shift from (j + 64)-th bit to (i + 64)-th bit. + SCOPED_TRACE(::testing::Message() << "i = " << i << "; j = " << j); + EXPECT_EQ(absl::MakeInt128(uint64_t{1} << i, 0), + absl::MakeInt128(uint64_t{1} << j, 0) >> (j - i)); + EXPECT_EQ(absl::MakeInt128(uint64_t{1} << i, 0), + absl::MakeInt128(uint64_t{1} << j, 0) >>= (j - i)); + } + } +} + +TEST(Int128, NumericLimitsTest) { + static_assert(std::numeric_limits::is_specialized, ""); + static_assert(std::numeric_limits::is_signed, ""); + static_assert(std::numeric_limits::is_integer, ""); + EXPECT_EQ(static_cast(127 * std::log10(2)), + std::numeric_limits::digits10); + EXPECT_EQ(absl::Int128Min(), std::numeric_limits::min()); + EXPECT_EQ(absl::Int128Min(), std::numeric_limits::lowest()); + EXPECT_EQ(absl::Int128Max(), std::numeric_limits::max()); +} + } // namespace diff --git a/absl/random/internal/platform.h b/absl/random/internal/platform.h index a5a42cbb80e..bbdb4e62081 100644 --- a/absl/random/internal/platform.h +++ b/absl/random/internal/platform.h @@ -162,7 +162,8 @@ // iOS does not support dispatch, even on x86, since applications // should be bundled as fat binaries, with a different build tailored for // each specific supported platform/architecture. -#if defined(__APPLE__) && (TARGET_OS_IPHONE || TARGET_OS_IPHONE_SIMULATOR) +#if (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) || \ + (defined(TARGET_OS_IPHONE_SIMULATOR) && TARGET_OS_IPHONE_SIMULATOR) #undef ABSL_RANDOM_INTERNAL_AES_DISPATCH #define ABSL_RANDOM_INTERNAL_AES_DISPATCH 0 #endif diff --git a/absl/strings/internal/str_format/arg.cc b/absl/strings/internal/str_format/arg.cc index b40be8ff382..b567a5c5bcf 100644 --- a/absl/strings/internal/str_format/arg.cc +++ b/absl/strings/internal/str_format/arg.cc @@ -32,6 +32,10 @@ void ReducePadding(size_t n, size_t *capacity) { template struct MakeUnsigned : std::make_unsigned {}; template <> +struct MakeUnsigned { + using type = absl::uint128; +}; +template <> struct MakeUnsigned { using type = absl::uint128; }; @@ -39,6 +43,8 @@ struct MakeUnsigned { template struct IsSigned : std::is_signed {}; template <> +struct IsSigned : std::true_type {}; +template <> struct IsSigned : std::false_type {}; class ConvertedIntInfo { @@ -363,6 +369,11 @@ IntegralConvertResult FormatConvertImpl(unsigned long long v, // NOLINT FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } +IntegralConvertResult FormatConvertImpl(absl::int128 v, + const ConversionSpec conv, + FormatSinkImpl *sink) { + return {ConvertIntArg(v, conv, sink)}; +} IntegralConvertResult FormatConvertImpl(absl::uint128 v, const ConversionSpec conv, FormatSinkImpl *sink) { @@ -372,6 +383,7 @@ IntegralConvertResult FormatConvertImpl(absl::uint128 v, ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_(); + } // namespace str_format_internal } // namespace absl diff --git a/absl/strings/internal/str_format/arg.h b/absl/strings/internal/str_format/arg.h index 5cb3a14d683..2119ca0036e 100644 --- a/absl/strings/internal/str_format/arg.h +++ b/absl/strings/internal/str_format/arg.h @@ -144,6 +144,8 @@ IntegralConvertResult FormatConvertImpl(long long v, // NOLINT IntegralConvertResult FormatConvertImpl(unsigned long long v, // NOLINT ConversionSpec conv, FormatSinkImpl* sink); +IntegralConvertResult FormatConvertImpl(int128 v, ConversionSpec conv, + FormatSinkImpl* sink); IntegralConvertResult FormatConvertImpl(uint128 v, ConversionSpec conv, FormatSinkImpl* sink); template ::value, int> = 0> @@ -408,6 +410,7 @@ class FormatArgImpl { __VA_ARGS__); \ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(unsigned long long, /* NOLINT */ \ __VA_ARGS__); \ + ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(int128, __VA_ARGS__); \ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(uint128, __VA_ARGS__); \ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(float, __VA_ARGS__); \ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(double, __VA_ARGS__); \ @@ -418,6 +421,7 @@ class FormatArgImpl { ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_(extern); + } // namespace str_format_internal } // namespace absl diff --git a/absl/strings/internal/str_format/convert_test.cc b/absl/strings/internal/str_format/convert_test.cc index ab8d5391411..5f198059fe5 100644 --- a/absl/strings/internal/str_format/convert_test.cc +++ b/absl/strings/internal/str_format/convert_test.cc @@ -390,7 +390,6 @@ typedef ::testing::Types< AllIntTypes; INSTANTIATE_TYPED_TEST_CASE_P(TypedFormatConvertTestWithAllIntTypes, TypedFormatConvertTest, AllIntTypes); - TEST_F(FormatConvertTest, VectorBool) { // Make sure vector's values behave as bools. std::vector v = {true, false}; @@ -402,6 +401,42 @@ TEST_F(FormatConvertTest, VectorBool) { FormatArgImpl(cv[0]), FormatArgImpl(cv[1])}))); } + +TEST_F(FormatConvertTest, Int128) { + absl::int128 positive = static_cast(0x1234567890abcdef) * 1979; + absl::int128 negative = -positive; + absl::int128 max = absl::Int128Max(), min = absl::Int128Min(); + const FormatArgImpl args[] = {FormatArgImpl(positive), + FormatArgImpl(negative), FormatArgImpl(max), + FormatArgImpl(min)}; + + struct Case { + const char* format; + const char* expected; + } cases[] = { + {"%1$d", "2595989796776606496405"}, + {"%1$30d", " 2595989796776606496405"}, + {"%1$-30d", "2595989796776606496405 "}, + {"%1$u", "2595989796776606496405"}, + {"%1$x", "8cba9876066020f695"}, + {"%2$d", "-2595989796776606496405"}, + {"%2$30d", " -2595989796776606496405"}, + {"%2$-30d", "-2595989796776606496405 "}, + {"%2$u", "340282366920938460867384810655161715051"}, + {"%2$x", "ffffffffffffff73456789f99fdf096b"}, + {"%3$d", "170141183460469231731687303715884105727"}, + {"%3$u", "170141183460469231731687303715884105727"}, + {"%3$x", "7fffffffffffffffffffffffffffffff"}, + {"%4$d", "-170141183460469231731687303715884105728"}, + {"%4$x", "80000000000000000000000000000000"}, + }; + + for (auto c : cases) { + UntypedFormatSpecImpl format(c.format); + EXPECT_EQ(c.expected, FormatPack(format, absl::MakeSpan(args))); + } +} + TEST_F(FormatConvertTest, Uint128) { absl::uint128 v = static_cast(0x1234567890abcdef) * 1979; absl::uint128 max = absl::Uint128Max(); diff --git a/absl/strings/string_view.h b/absl/strings/string_view.h index 07075e80ec6..6a650874aa2 100644 --- a/absl/strings/string_view.h +++ b/absl/strings/string_view.h @@ -290,7 +290,8 @@ class string_view { constexpr const_reference at(size_type i) const { return ABSL_PREDICT_TRUE(i < size()) ? ptr_[i] - : (base_internal::ThrowStdOutOfRange("absl::string_view::at"), + : ((void)base_internal::ThrowStdOutOfRange( + "absl::string_view::at"), ptr_[i]); } @@ -511,7 +512,7 @@ class string_view { (std::numeric_limits::max)(); static constexpr size_type CheckLengthInternal(size_type len) { - return ABSL_ASSERT(len <= kMaxSize), len; + return (void)ABSL_ASSERT(len <= kMaxSize), len; } static constexpr size_type StrlenInternal(const char* str) { diff --git a/absl/time/internal/test_util.cc b/absl/time/internal/test_util.cc index 59166a7c82d..fbddbb73369 100644 --- a/absl/time/internal/test_util.cc +++ b/absl/time/internal/test_util.cc @@ -116,7 +116,10 @@ std::unique_ptr TestFactory( } // namespace +#if !defined(__MINGW32__) +// MinGW does not support the weak symbol extension mechanism. ZoneInfoSourceFactory zone_info_source_factory = TestFactory; +#endif } // namespace cctz_extension } // namespace time_internal diff --git a/absl/time/time.h b/absl/time/time.h index 46ac26b3c0d..be0648134ef 100644 --- a/absl/time/time.h +++ b/absl/time/time.h @@ -1412,8 +1412,7 @@ constexpr Duration FromInt64(int64_t v, std::ratio<3600>) { // IsValidRep64(0) is true if the expression `int64_t{std::declval()}` is // valid. That is, if a T can be assigned to an int64_t without narrowing. template -constexpr auto IsValidRep64(int) - -> decltype(int64_t{std::declval()}, bool()) { +constexpr auto IsValidRep64(int) -> decltype(int64_t{std::declval()} == 0) { return true; } template From 8ba96a8244bbe334d09542e92d566673a65c1f78 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Tue, 19 Nov 2019 14:43:59 -0800 Subject: [PATCH 0020/3164] Export of internal Abseil changes -- 87f85071a96c6d10f471cdf8a8c2d6c550c926e0 by Matt Kulukundis : Expose ABSL_HAVE_SOURCE_LOCATION_CURRENT as a public api. PiperOrigin-RevId: 281383411 GitOrigin-RevId: 87f85071a96c6d10f471cdf8a8c2d6c550c926e0 Change-Id: I4dfc18a5ba8e064bf9b749892aa8293470459e6b --- absl/base/config.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/absl/base/config.h b/absl/base/config.h index 46a84f1a76f..556b186cb94 100644 --- a/absl/base/config.h +++ b/absl/base/config.h @@ -85,6 +85,12 @@ #define ABSL_HAVE_BUILTIN(x) 0 #endif +#if defined(__is_identifier) +#define ABSL_INTERNAL_HAS_KEYWORD(x) !(__is_identifier(x)) +#else +#define ABSL_INTERNAL_HAS_KEYWORD(x) 0 +#endif + // ABSL_HAVE_TLS is defined to 1 when __thread should be supported. // We assume __thread is supported on Linux when compiled with Clang or compiled // against libstdc++ with _GLIBCXX_HAVE_TLS defined. @@ -133,6 +139,17 @@ #define ABSL_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE 1 #endif +// ABSL_HAVE_SOURCE_LOCATION_CURRENT +// +// Indicates whether `absl::SourceLocation::current()` will return useful +// information in some contexts. +#ifndef ABSL_HAVE_SOURCE_LOCATION_CURRENT +#if ABSL_INTERNAL_HAS_KEYWORD(__builtin_LINE) && \ + ABSL_INTERNAL_HAS_KEYWORD(__builtin_FILE) +#define ABSL_HAVE_SOURCE_LOCATION_CURRENT 1 +#endif +#endif + // ABSL_HAVE_THREAD_LOCAL // // Checks whether C++11's `thread_local` storage duration specifier is @@ -539,4 +556,6 @@ #define ABSL_INTERNAL_MSVC_2017_DBG_MODE #endif +#undef ABSL_INTERNAL_HAS_KEYWORD + #endif // ABSL_BASE_CONFIG_H_ From bcaae6009c0833b73c6fa7bdd972921d8081a724 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Wed, 20 Nov 2019 13:55:25 -0800 Subject: [PATCH 0021/3164] Export of internal Abseil changes -- 4503a1945d61540d3f7dd9ee2399297fbd423b82 by Andy Soffer : Releasing absl::MockingBitGen and absl::BitGenRef PiperOrigin-RevId: 281594380 -- 2cb926fd9e39b5fa24c9541bfafcf18916cf7df2 by Abseil Team : Explicitly export files needed by other packages PiperOrigin-RevId: 281482304 GitOrigin-RevId: 4503a1945d61540d3f7dd9ee2399297fbd423b82 Change-Id: Id9f694eaaa23f42de817c8e8a28e6f86444f5637 --- absl/random/BUILD.bazel | 92 ++++++ absl/random/CMakeLists.txt | 126 +++++++ absl/random/bit_gen_ref.h | 151 +++++++++ absl/random/bit_gen_ref_test.cc | 99 ++++++ absl/random/internal/BUILD.bazel | 23 ++ absl/random/internal/mock_overload_set.h | 89 +++++ absl/random/internal/mocking_bit_gen_base.h | 118 +++++++ absl/random/mock_distributions.h | 259 +++++++++++++++ absl/random/mock_distributions_test.cc | 72 ++++ absl/random/mocking_bit_gen.cc | 28 ++ absl/random/mocking_bit_gen.h | 194 +++++++++++ absl/random/mocking_bit_gen_test.cc | 347 ++++++++++++++++++++ 12 files changed, 1598 insertions(+) create mode 100644 absl/random/bit_gen_ref.h create mode 100644 absl/random/bit_gen_ref_test.cc create mode 100644 absl/random/internal/mock_overload_set.h create mode 100644 absl/random/internal/mocking_bit_gen_base.h create mode 100644 absl/random/mock_distributions.h create mode 100644 absl/random/mock_distributions_test.cc create mode 100644 absl/random/mocking_bit_gen.cc create mode 100644 absl/random/mocking_bit_gen.h create mode 100644 absl/random/mocking_bit_gen_test.cc diff --git a/absl/random/BUILD.bazel b/absl/random/BUILD.bazel index c3520df8dcc..2585b39742e 100644 --- a/absl/random/BUILD.bazel +++ b/absl/random/BUILD.bazel @@ -110,6 +110,58 @@ cc_library( ], ) +cc_library( + name = "bit_gen_ref", + hdrs = ["bit_gen_ref.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + "//absl/base:core_headers", + "//absl/meta:type_traits", + "//absl/random/internal:distribution_caller", + "//absl/random/internal:fast_uniform_bits", + "//absl/random/internal:mocking_bit_gen_base", + ], +) + +cc_library( + name = "mock_distributions", + testonly = 1, + hdrs = ["mock_distributions.h"], + deps = [ + ":distributions", + ":mocking_bit_gen", + "//absl/meta:type_traits", + "//absl/random/internal:mock_overload_set", + "@com_google_googletest//:gtest", + ], +) + +cc_library( + name = "mocking_bit_gen", + testonly = 1, + srcs = [ + "mocking_bit_gen.cc", + ], + hdrs = [ + "mocking_bit_gen.h", + ], + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":distributions", + "//absl/base:raw_logging_internal", + "//absl/container:flat_hash_map", + "//absl/meta:type_traits", + "//absl/random/internal:distribution_caller", + "//absl/random/internal:mocking_bit_gen_base", + "//absl/strings", + "//absl/types:span", + "//absl/types:variant", + "//absl/utility", + "@com_google_googletest//:gtest", + ], +) + cc_test( name = "bernoulli_distribution_test", size = "small", @@ -345,6 +397,46 @@ cc_test( ], ) +cc_test( + name = "bit_gen_ref_test", + size = "small", + srcs = ["bit_gen_ref_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":bit_gen_ref", + ":random", + "//absl/random/internal:sequence_urbg", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "mocking_bit_gen_test", + size = "small", + srcs = ["mocking_bit_gen_test.cc"], + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":bit_gen_ref", + ":mock_distributions", + ":mocking_bit_gen", + ":random", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "mock_distributions_test", + size = "small", + srcs = ["mock_distributions_test.cc"], + deps = [ + ":mock_distributions", + ":mocking_bit_gen", + ":random", + "@com_google_googletest//:gtest_main", + ], +) + cc_test( name = "examples_test", size = "small", diff --git a/absl/random/CMakeLists.txt b/absl/random/CMakeLists.txt index 264a6f3c6ab..13e96357ffd 100644 --- a/absl/random/CMakeLists.txt +++ b/absl/random/CMakeLists.txt @@ -32,6 +32,132 @@ absl_cc_library( absl::random_seed_sequences ) +absl_cc_library( + NAME + random_bit_gen_ref + HDRS + "bit_gen_ref.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::core_headers + absl::random_internal_distribution_caller + absl::random_internal_fast_uniform_bits + absl::random_internal_mocking_bit_gen_base + absl::type_traits +) + +absl_cc_test( + NAME + random_bit_gen_ref_test + SRCS + "bit_gen_ref_test.cc" + COPTS + ${ABSL_TEST_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::random_bit_gen_ref + absl::random_random + absl::random_internal_sequence_urbg + gmock + gtest_main +) + +# Internal-only target, do not depend on directly. +absl_cc_library( + NAME + random_internal_mocking_bit_gen_base + HDRS + "internal/mocking_bit_gen_base.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::random_random + absl::strings +) + +# Internal-only target, do not depend on directly. +absl_cc_library( + NAME + random_internal_mock_overload_set + HDRS + "internal/mock_overload_set.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::random_mocking_bit_gen + TESTONLY +) + +absl_cc_library( + NAME + random_mocking_bit_gen + HDRS + "mock_distributions.h" + "mocking_bit_gen.h" + SRCS + "mocking_bit_gen.cc" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::flat_hash_map + absl::raw_logging_internal + absl::random_distributions + absl::random_internal_distribution_caller + absl::random_internal_mocking_bit_gen_base + absl::random_internal_mock_overload_set + absl::strings + absl::span + absl::type_traits + absl::utility + absl::variant + gmock + gtest + TESTONLY +) + +absl_cc_test( + NAME + random_mock_distributions_test + SRCS + "mock_distributions_test.cc" + COPTS + ${ABSL_TEST_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::random_mocking_bit_gen + absl::random_random + gmock + gtest_main +) + +absl_cc_test( + NAME + random_mocking_bit_gen_test + SRCS + "mocking_bit_gen_test.cc" + COPTS + ${ABSL_TEST_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::random_bit_gen_ref + absl::random_mocking_bit_gen + absl::random_random + gmock + gtest_main +) + absl_cc_library( NAME random_distributions diff --git a/absl/random/bit_gen_ref.h b/absl/random/bit_gen_ref.h new file mode 100644 index 00000000000..00e904f85ec --- /dev/null +++ b/absl/random/bit_gen_ref.h @@ -0,0 +1,151 @@ +// +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: bit_gen_ref.h +// ----------------------------------------------------------------------------- +// +// This header defines a bit generator "reference" class, for use in interfaces +// that take both Abseil (e.g. `absl::BitGen`) and standard library (e.g. +// `std::mt19937`) bit generators. + +#ifndef ABSL_RANDOM_BIT_GEN_REF_H_ +#define ABSL_RANDOM_BIT_GEN_REF_H_ + +#include "absl/base/macros.h" +#include "absl/meta/type_traits.h" +#include "absl/random/internal/distribution_caller.h" +#include "absl/random/internal/fast_uniform_bits.h" +#include "absl/random/internal/mocking_bit_gen_base.h" + +namespace absl { +namespace random_internal { + +template +struct is_urbg : std::false_type {}; + +template +struct is_urbg< + URBG, + absl::enable_if_t::type>::value>, + absl::enable_if_t::type>::value>, + absl::enable_if_t()())>::type>::value>> + : std::true_type {}; + +} // namespace random_internal + +// ----------------------------------------------------------------------------- +// absl::BitGenRef +// ----------------------------------------------------------------------------- +// +// `absl::BitGenRef` is a type-erasing class that provides a generator-agnostic +// non-owning "reference" interface for use in place of any specific uniform +// random bit generator (URBG). This class may be used for both Abseil +// (e.g. `absl::BitGen`, `absl::InsecureBitGen`) and Standard library (e.g +// `std::mt19937`, `std::minstd_rand`) bit generators. +// +// Like other reference classes, `absl::BitGenRef` does not own the +// underlying bit generator, and the underlying instance must outlive the +// `absl::BitGenRef`. +// +// `absl::BitGenRef` is particularly useful when used with an +// `absl::MockingBitGen` to test specific paths in functions which use random +// values. +// +// Example: +// void TakesBitGenRef(absl::BitGenRef gen) { +// int x = absl::Uniform(gen, 0, 1000); +// } +// +class BitGenRef { + public: + using result_type = uint64_t; + + BitGenRef(const absl::BitGenRef&) = default; + BitGenRef(absl::BitGenRef&&) = default; + BitGenRef& operator=(const absl::BitGenRef&) = default; + BitGenRef& operator=(absl::BitGenRef&&) = default; + + template ::value && + random_internal::is_urbg::value)>* = nullptr> + BitGenRef(URBG& gen) // NOLINT + : mocked_gen_ptr_(MakeMockPointer(&gen)), + t_erased_gen_ptr_(reinterpret_cast(&gen)), + generate_impl_fn_(ImplFn) { + } + + static constexpr result_type(min)() { + return (std::numeric_limits::min)(); + } + + static constexpr result_type(max)() { + return (std::numeric_limits::max)(); + } + + result_type operator()() { return generate_impl_fn_(t_erased_gen_ptr_); } + + private: + friend struct absl::random_internal::DistributionCaller; + using impl_fn = result_type (*)(uintptr_t); + using mocker_base_t = absl::random_internal::MockingBitGenBase; + + // Convert an arbitrary URBG pointer into either a valid mocker_base_t + // pointer or a nullptr. + static inline mocker_base_t* MakeMockPointer(mocker_base_t* t) { return t; } + static inline mocker_base_t* MakeMockPointer(void*) { return nullptr; } + + template + static result_type ImplFn(uintptr_t ptr) { + // Ensure that the return values from operator() fill the entire + // range promised by result_type, min() and max(). + absl::random_internal::FastUniformBits fast_uniform_bits; + return fast_uniform_bits(*reinterpret_cast(ptr)); + } + + mocker_base_t* mocked_gen_ptr_; + uintptr_t t_erased_gen_ptr_; + impl_fn generate_impl_fn_; +}; + +namespace random_internal { + +template <> +struct DistributionCaller { + template + static typename DistrT::result_type Call(absl::BitGenRef* gen_ref, + Args&&... args) { + auto* mock_ptr = gen_ref->mocked_gen_ptr_; + if (mock_ptr == nullptr) { + DistrT dist(std::forward(args)...); + return dist(*gen_ref); + } else { + return mock_ptr->template Call( + std::forward(args)...); + } + } +}; + +} // namespace random_internal +} // namespace absl + +#endif // ABSL_RANDOM_BIT_GEN_REF_H_ diff --git a/absl/random/bit_gen_ref_test.cc b/absl/random/bit_gen_ref_test.cc new file mode 100644 index 00000000000..bc02ca5c162 --- /dev/null +++ b/absl/random/bit_gen_ref_test.cc @@ -0,0 +1,99 @@ +// +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#include "absl/random/bit_gen_ref.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/random/internal/sequence_urbg.h" +#include "absl/random/random.h" + +namespace absl { + +class ConstBitGen : public absl::random_internal::MockingBitGenBase { + bool CallImpl(const std::type_info&, void*, void* result) override { + *static_cast(result) = 42; + return true; + } +}; + +namespace random_internal { +template <> +struct DistributionCaller { + template + static typename DistrT::result_type Call(ConstBitGen* gen, Args&&... args) { + return gen->template Call(std::forward(args)...); + } +}; +} // namespace random_internal + +namespace { +int FnTest(absl::BitGenRef gen_ref) { return absl::Uniform(gen_ref, 1, 7); } + +template +class BitGenRefTest : public testing::Test {}; + +using BitGenTypes = + ::testing::Types; +TYPED_TEST_SUITE(BitGenRefTest, BitGenTypes); + +TYPED_TEST(BitGenRefTest, BasicTest) { + TypeParam gen; + auto x = FnTest(gen); + EXPECT_NEAR(x, 4, 3); +} + +TYPED_TEST(BitGenRefTest, Copyable) { + TypeParam gen; + absl::BitGenRef gen_ref(gen); + FnTest(gen_ref); // Copy +} + +TEST(BitGenRefTest, PassThroughEquivalence) { + // sequence_urbg returns 64-bit results. + absl::random_internal::sequence_urbg urbg( + {0x0003eb76f6f7f755ull, 0xFFCEA50FDB2F953Bull, 0xC332DDEFBE6C5AA5ull, + 0x6558218568AB9702ull, 0x2AEF7DAD5B6E2F84ull, 0x1521B62829076170ull, + 0xECDD4775619F1510ull, 0x13CCA830EB61BD96ull, 0x0334FE1EAA0363CFull, + 0xB5735C904C70A239ull, 0xD59E9E0BCBAADE14ull, 0xEECC86BC60622CA7ull}); + + std::vector output(12); + + { + absl::BitGenRef view(urbg); + for (auto& v : output) { + v = view(); + } + } + + std::vector expected( + {0x0003eb76f6f7f755ull, 0xFFCEA50FDB2F953Bull, 0xC332DDEFBE6C5AA5ull, + 0x6558218568AB9702ull, 0x2AEF7DAD5B6E2F84ull, 0x1521B62829076170ull, + 0xECDD4775619F1510ull, 0x13CCA830EB61BD96ull, 0x0334FE1EAA0363CFull, + 0xB5735C904C70A239ull, 0xD59E9E0BCBAADE14ull, 0xEECC86BC60622CA7ull}); + + EXPECT_THAT(output, testing::Eq(expected)); +} + +TEST(BitGenRefTest, MockingBitGenBaseOverrides) { + ConstBitGen const_gen; + EXPECT_EQ(FnTest(const_gen), 42); + + absl::BitGenRef gen_ref(const_gen); + EXPECT_EQ(FnTest(gen_ref), 42); // Copy +} +} // namespace +} // namespace absl diff --git a/absl/random/internal/BUILD.bazel b/absl/random/internal/BUILD.bazel index cc9bc013bc9..5026e2b2722 100644 --- a/absl/random/internal/BUILD.bazel +++ b/absl/random/internal/BUILD.bazel @@ -493,6 +493,29 @@ cc_test( ], ) +cc_library( + name = "mocking_bit_gen_base", + hdrs = ["mocking_bit_gen_base.h"], + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + "//absl/random", + "//absl/strings", + ], +) + +cc_library( + name = "mock_overload_set", + testonly = 1, + hdrs = ["mock_overload_set.h"], + visibility = [ + "//absl/random:__pkg__", + ], + deps = [ + "//absl/random:mocking_bit_gen", + "@com_google_googletest//:gtest", + ], +) + cc_test( name = "nonsecure_base_test", size = "small", diff --git a/absl/random/internal/mock_overload_set.h b/absl/random/internal/mock_overload_set.h new file mode 100644 index 00000000000..539313d73c9 --- /dev/null +++ b/absl/random/internal/mock_overload_set.h @@ -0,0 +1,89 @@ +// +// Copyright 2019 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_RANDOM_INTERNAL_MOCK_OVERLOAD_SET_H_ +#define ABSL_RANDOM_INTERNAL_MOCK_OVERLOAD_SET_H_ + +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/random/mocking_bit_gen.h" + +namespace absl { +namespace random_internal { + +template +struct MockSingleOverload; + +// MockSingleOverload +// +// MockSingleOverload hooks in to gMock's `ON_CALL` and `EXPECT_CALL` macros. +// EXPECT_CALL(mock_single_overload, Call(...))` will expand to a call to +// `mock_single_overload.gmock_Call(...)`. Because expectations are stored on +// the MockingBitGen (an argument passed inside `Call(...)`), this forwards to +// arguments to Mocking::Register. +template +struct MockSingleOverload { + static_assert(std::is_same::value, + "Overload signature must have return type matching the " + "distributions result type."); + auto gmock_Call( + absl::MockingBitGen& gen, // NOLINT(google-runtime-references) + const ::testing::Matcher&... args) + -> decltype(gen.Register(args...)) { + return gen.Register(args...); + } +}; + +template +struct MockSingleOverload { + static_assert(std::is_same::value, + "Overload signature must have return type matching the " + "distributions result type."); + auto gmock_Call( + const ::testing::Matcher& arg, + absl::MockingBitGen& gen, // NOLINT(google-runtime-references) + const ::testing::Matcher&... args) + -> decltype(gen.Register(arg, args...)) { + return gen.Register(arg, args...); + } +}; + +// MockOverloadSet +// +// MockOverloadSet takes a distribution and a collection of signatures and +// performs overload resolution amongst all the overloads. This makes +// `EXPECT_CALL(mock_overload_set, Call(...))` expand and do overload resolution +// correctly. +template +struct MockOverloadSet; + +template +struct MockOverloadSet : public MockSingleOverload { + using MockSingleOverload::gmock_Call; +}; + +template +struct MockOverloadSet + : public MockSingleOverload, + public MockOverloadSet { + using MockSingleOverload::gmock_Call; + using MockOverloadSet::gmock_Call; +}; + +} // namespace random_internal +} // namespace absl +#endif // ABSL_RANDOM_INTERNAL_MOCK_OVERLOAD_SET_H_ diff --git a/absl/random/internal/mocking_bit_gen_base.h b/absl/random/internal/mocking_bit_gen_base.h new file mode 100644 index 00000000000..aff2ba6df3e --- /dev/null +++ b/absl/random/internal/mocking_bit_gen_base.h @@ -0,0 +1,118 @@ +// +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#ifndef ABSL_RANDOM_INTERNAL_MOCKING_BIT_GEN_BASE_H_ +#define ABSL_RANDOM_INTERNAL_MOCKING_BIT_GEN_BASE_H_ + +#include +#include +#include +#include + +#include "absl/random/random.h" +#include "absl/strings/str_cat.h" + +namespace absl { +namespace random_internal { + +// MockingBitGenExpectationFormatter is invoked to format unsatisfied mocks +// and remaining results into a description string. +template +struct MockingBitGenExpectationFormatter { + std::string operator()(absl::string_view args) { + return absl::StrCat(FormatT::FunctionName(), "(", args, ")"); + } +}; + +// MockingBitGenCallFormatter is invoked to format each distribution call +// into a description string for the mock log. +template +struct MockingBitGenCallFormatter { + std::string operator()(const DistrT& dist, + const typename DistrT::result_type& result) { + return absl::StrCat( + FormatT::FunctionName(), "(", FormatT::FormatArgs(dist), ") => {", + FormatT::FormatResults(absl::MakeSpan(&result, 1)), "}"); + } +}; + +class MockingBitGenBase { + template + friend struct DistributionCaller; + using generator_type = absl::BitGen; + + public: + // URBG interface + using result_type = generator_type::result_type; + static constexpr result_type(min)() { return (generator_type::min)(); } + static constexpr result_type(max)() { return (generator_type::max)(); } + result_type operator()() { return gen_(); } + + MockingBitGenBase() : gen_(), observed_call_log_() {} + virtual ~MockingBitGenBase() = default; + + protected: + const std::deque& observed_call_log() { + return observed_call_log_; + } + + // CallImpl is the type-erased virtual dispatch. + // The type of dist is always distribution, + // The type of result is always distribution::result_type. + virtual bool CallImpl(const std::type_info& distr_type, void* dist_args, + void* result) = 0; + + template + static const std::type_info& GetTypeId() { + return typeid(std::pair, absl::decay_t>); + } + + // Call the generating distribution function. + // Invoked by DistributionCaller<>::Call. + // DistT is the distribution type. + // FormatT is the distribution formatter traits type. + template + typename DistrT::result_type Call(Args&&... args) { + using distr_result_type = typename DistrT::result_type; + using ArgTupleT = std::tuple...>; + + ArgTupleT arg_tuple(std::forward(args)...); + auto dist = absl::make_from_tuple(arg_tuple); + + distr_result_type result{}; + bool found_match = + CallImpl(GetTypeId(), &arg_tuple, &result); + + if (!found_match) { + result = dist(gen_); + } + + // TODO(asoffer): Forwarding the args through means we no longer need to + // extract them from the from the distribution in formatter traits. We can + // just StrJoin them. + observed_call_log_.push_back( + MockingBitGenCallFormatter{}(dist, result)); + return result; + } + + private: + generator_type gen_; + std::deque observed_call_log_; +}; // namespace random_internal + +} // namespace random_internal +} // namespace absl + +#endif // ABSL_RANDOM_INTERNAL_MOCKING_BIT_GEN_BASE_H_ diff --git a/absl/random/mock_distributions.h b/absl/random/mock_distributions.h new file mode 100644 index 00000000000..1af98a24a7f --- /dev/null +++ b/absl/random/mock_distributions.h @@ -0,0 +1,259 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: mock_distributions.h +// ----------------------------------------------------------------------------- +// +// This file contains mock distribution functions for use alongside an +// `absl::MockingBitGen` object within the Googletest testing framework. Such +// mocks are useful to provide deterministic values as return values within +// (otherwise random) Abseil distribution functions. +// +// The return type of each function is a mock expectation object which +// is used to set the match result. +// +// More information about the Googletest testing framework is available at +// https://github.com/google/googletest +// +// Example: +// +// absl::MockingBitGen mock; +// EXPECT_CALL(absl::MockUniform(), Call(mock, 1, 1000)) +// .WillRepeatedly(testing::ReturnRoundRobin({20, 40})); +// +// EXPECT_EQ(absl::Uniform(gen, 1, 1000), 20); +// EXPECT_EQ(absl::Uniform(gen, 1, 1000), 40); +// EXPECT_EQ(absl::Uniform(gen, 1, 1000), 20); +// EXPECT_EQ(absl::Uniform(gen, 1, 1000), 40); + +#ifndef ABSL_RANDOM_MOCK_DISTRIBUTIONS_H_ +#define ABSL_RANDOM_MOCK_DISTRIBUTIONS_H_ + +#include +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/meta/type_traits.h" +#include "absl/random/distributions.h" +#include "absl/random/internal/mock_overload_set.h" +#include "absl/random/mocking_bit_gen.h" + +namespace absl { + +// ----------------------------------------------------------------------------- +// absl::MockUniform +// ----------------------------------------------------------------------------- +// +// Matches calls to absl::Uniform. +// +// `absl::MockUniform` is a class template used in conjunction with Googletest's +// `ON_CALL()` and `EXPECT_CALL()` macros. To use it, default-construct an +// instance of it inside `ON_CALL()` or `EXPECT_CALL()`, and use `Call(...)` the +// same way one would define mocks on a Googletest `MockFunction()`. +// +// Example: +// +// absl::MockingBitGen mock; +// EXPECT_CALL(absl::MockUniform(), Call(mock)) +// .WillOnce(Return(123456)); +// auto x = absl::Uniform(mock); +// assert(x == 123456) +// +template +using MockUniform = random_internal::MockOverloadSet< + random_internal::UniformDistributionWrapper, + R(IntervalClosedOpenTag, MockingBitGen&, R, R), + R(IntervalClosedClosedTag, MockingBitGen&, R, R), + R(IntervalOpenOpenTag, MockingBitGen&, R, R), + R(IntervalOpenClosedTag, MockingBitGen&, R, R), R(MockingBitGen&, R, R), + R(MockingBitGen&)>; + +// ----------------------------------------------------------------------------- +// absl::MockBernoulli +// ----------------------------------------------------------------------------- +// +// Matches calls to absl::Bernoulli. +// +// `absl::MockBernoulli` is a class used in conjunction with Googletest's +// `ON_CALL()` and `EXPECT_CALL()` macros. To use it, default-construct an +// instance of it inside `ON_CALL()` or `EXPECT_CALL()`, and use `Call(...)` the +// same way one would define mocks on a Googletest `MockFunction()`. +// +// Example: +// +// absl::MockingBitGen mock; +// EXPECT_CALL(absl::MockBernoulli(), Call(mock, testing::_)) +// .WillOnce(Return(false)); +// assert(absl::Bernoulli(mock, 0.5) == false); +// +using MockBernoulli = + random_internal::MockOverloadSet; + +// ----------------------------------------------------------------------------- +// absl::MockBeta +// ----------------------------------------------------------------------------- +// +// Matches calls to absl::Beta. +// +// `absl::MockBeta` is a class used in conjunction with Googletest's `ON_CALL()` +// and `EXPECT_CALL()` macros. To use it, default-construct an instance of it +// inside `ON_CALL()` or `EXPECT_CALL()`, and use `Call(...)` the same way one +// would define mocks on a Googletest `MockFunction()`. +// +// Example: +// +// absl::MockingBitGen mock; +// EXPECT_CALL(absl::MockBeta(), Call(mock, 3.0, 2.0)) +// .WillOnce(Return(0.567)); +// auto x = absl::Beta(mock, 3.0, 2.0); +// assert(x == 0.567); +// +template +using MockBeta = + random_internal::MockOverloadSet, + RealType(MockingBitGen&, RealType, + RealType)>; + +// ----------------------------------------------------------------------------- +// absl::MockExponential +// ----------------------------------------------------------------------------- +// +// Matches calls to absl::Exponential. +// +// `absl::MockExponential` is a class template used in conjunction with +// Googletest's `ON_CALL()` and `EXPECT_CALL()` macros. To use it, +// default-construct an instance of it inside `ON_CALL()` or `EXPECT_CALL()`, +// and use `Call(...)` the same way one would define mocks on a +// Googletest `MockFunction()`. +// +// Example: +// +// absl::MockingBitGen mock; +// EXPECT_CALL(absl::MockExponential(), Call(mock, 0.5)) +// .WillOnce(Return(12.3456789)); +// auto x = absl::Exponential(mock, 0.5); +// assert(x == 12.3456789) +// +template +using MockExponential = + random_internal::MockOverloadSet, + RealType(MockingBitGen&, RealType)>; + +// ----------------------------------------------------------------------------- +// absl::MockGaussian +// ----------------------------------------------------------------------------- +// +// Matches calls to absl::Gaussian. +// +// `absl::MockGaussian` is a class template used in conjunction with +// Googletest's `ON_CALL()` and `EXPECT_CALL()` macros. To use it, +// default-construct an instance of it inside `ON_CALL()` or `EXPECT_CALL()`, +// and use `Call(...)` the same way one would define mocks on a +// Googletest `MockFunction()`. +// +// Example: +// +// absl::MockingBitGen mock; +// EXPECT_CALL(absl::MockGaussian(), Call(mock, 16.3, 3.3)) +// .WillOnce(Return(12.3456789)); +// auto x = absl::Gaussian(mock, 16.3, 3.3); +// assert(x == 12.3456789) +// +template +using MockGaussian = + random_internal::MockOverloadSet, + RealType(MockingBitGen&, RealType, + RealType)>; + +// ----------------------------------------------------------------------------- +// absl::MockLogUniform +// ----------------------------------------------------------------------------- +// +// Matches calls to absl::LogUniform. +// +// `absl::MockLogUniform` is a class template used in conjunction with +// Googletest's `ON_CALL()` and `EXPECT_CALL()` macros. To use it, +// default-construct an instance of it inside `ON_CALL()` or `EXPECT_CALL()`, +// and use `Call(...)` the same way one would define mocks on a +// Googletest `MockFunction()`. +// +// Example: +// +// absl::MockingBitGen mock; +// EXPECT_CALL(absl::MockLogUniform(), Call(mock, 10, 10000, 10)) +// .WillOnce(Return(1221)); +// auto x = absl::LogUniform(mock, 10, 10000, 10); +// assert(x == 1221) +// +template +using MockLogUniform = random_internal::MockOverloadSet< + absl::log_uniform_int_distribution, + IntType(MockingBitGen&, IntType, IntType, IntType)>; + +// ----------------------------------------------------------------------------- +// absl::MockPoisson +// ----------------------------------------------------------------------------- +// +// Matches calls to absl::Poisson. +// +// `absl::MockPoisson` is a class template used in conjunction with Googletest's +// `ON_CALL()` and `EXPECT_CALL()` macros. To use it, default-construct an +// instance of it inside `ON_CALL()` or `EXPECT_CALL()`, and use `Call(...)` the +// same way one would define mocks on a Googletest `MockFunction()`. +// +// Example: +// +// absl::MockingBitGen mock; +// EXPECT_CALL(absl::MockPoisson(), Call(mock, 2.0)) +// .WillOnce(Return(1221)); +// auto x = absl::Poisson(mock, 2.0); +// assert(x == 1221) +// +template +using MockPoisson = + random_internal::MockOverloadSet, + IntType(MockingBitGen&, double)>; + +// ----------------------------------------------------------------------------- +// absl::MockZipf +// ----------------------------------------------------------------------------- +// +// Matches calls to absl::Zipf. +// +// `absl::MockZipf` is a class template used in conjunction with Googletest's +// `ON_CALL()` and `EXPECT_CALL()` macros. To use it, default-construct an +// instance of it inside `ON_CALL()` or `EXPECT_CALL()`, and use `Call(...)` the +// same way one would define mocks on a Googletest `MockFunction()`. +// +// Example: +// +// absl::MockingBitGen mock; +// EXPECT_CALL(absl::MockZipf(), Call(mock, 1000000, 2.0, 1.0)) +// .WillOnce(Return(1221)); +// auto x = absl::Zipf(mock, 1000000, 2.0, 1.0); +// assert(x == 1221) +// +template +using MockZipf = + random_internal::MockOverloadSet, + IntType(MockingBitGen&, IntType, double, + double)>; + +} // namespace absl + +#endif // ABSL_RANDOM_MOCK_DISTRIBUTIONS_H_ diff --git a/absl/random/mock_distributions_test.cc b/absl/random/mock_distributions_test.cc new file mode 100644 index 00000000000..de23bafe1e5 --- /dev/null +++ b/absl/random/mock_distributions_test.cc @@ -0,0 +1,72 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/random/mock_distributions.h" + +#include "gtest/gtest.h" +#include "absl/random/mocking_bit_gen.h" +#include "absl/random/random.h" + +namespace { +using ::testing::Return; + +TEST(MockDistributions, Examples) { + absl::MockingBitGen gen; + + EXPECT_NE(absl::Uniform(gen, 1, 1000000), 20); + EXPECT_CALL(absl::MockUniform(), Call(gen, 1, 1000000)) + .WillOnce(Return(20)); + EXPECT_EQ(absl::Uniform(gen, 1, 1000000), 20); + + EXPECT_NE(absl::Uniform(gen, 0.0, 100.0), 5.0); + EXPECT_CALL(absl::MockUniform(), Call(gen, 0.0, 100.0)) + .WillOnce(Return(5.0)); + EXPECT_EQ(absl::Uniform(gen, 0.0, 100.0), 5.0); + + EXPECT_NE(absl::Exponential(gen, 1.0), 42); + EXPECT_CALL(absl::MockExponential(), Call(gen, 1.0)) + .WillOnce(Return(42)); + EXPECT_EQ(absl::Exponential(gen, 1.0), 42); + + EXPECT_NE(absl::Poisson(gen, 1.0), 500); + EXPECT_CALL(absl::MockPoisson(), Call(gen, 1.0)).WillOnce(Return(500)); + EXPECT_EQ(absl::Poisson(gen, 1.0), 500); + + EXPECT_NE(absl::Bernoulli(gen, 0.000001), true); + EXPECT_CALL(absl::MockBernoulli(), Call(gen, 0.000001)) + .WillOnce(Return(true)); + EXPECT_EQ(absl::Bernoulli(gen, 0.000001), true); + + EXPECT_NE(absl::Beta(gen, 3.0, 2.0), 0.567); + EXPECT_CALL(absl::MockBeta(), Call(gen, 3.0, 2.0)) + .WillOnce(Return(0.567)); + EXPECT_EQ(absl::Beta(gen, 3.0, 2.0), 0.567); + + EXPECT_NE(absl::Zipf(gen, 1000000, 2.0, 1.0), 1221); + EXPECT_CALL(absl::MockZipf(), Call(gen, 1000000, 2.0, 1.0)) + .WillOnce(Return(1221)); + EXPECT_EQ(absl::Zipf(gen, 1000000, 2.0, 1.0), 1221); + + EXPECT_NE(absl::Gaussian(gen, 0.0, 1.0), 0.001); + EXPECT_CALL(absl::MockGaussian(), Call(gen, 0.0, 1.0)) + .WillOnce(Return(0.001)); + EXPECT_EQ(absl::Gaussian(gen, 0.0, 1.0), 0.001); + + EXPECT_NE(absl::LogUniform(gen, 0, 1000000, 2), 2040); + EXPECT_CALL(absl::MockLogUniform(), Call(gen, 0, 1000000, 2)) + .WillOnce(Return(2040)); + EXPECT_EQ(absl::LogUniform(gen, 0, 1000000, 2), 2040); +} + +} // namespace diff --git a/absl/random/mocking_bit_gen.cc b/absl/random/mocking_bit_gen.cc new file mode 100644 index 00000000000..73144528a78 --- /dev/null +++ b/absl/random/mocking_bit_gen.cc @@ -0,0 +1,28 @@ +// +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#include "absl/random/mocking_bit_gen.h" + +#include + +namespace absl { +MockingBitGen::~MockingBitGen() { + + for (const auto& del : deleters_) { + del(); + } +} + +} // namespace absl diff --git a/absl/random/mocking_bit_gen.h b/absl/random/mocking_bit_gen.h new file mode 100644 index 00000000000..d1b524a993d --- /dev/null +++ b/absl/random/mocking_bit_gen.h @@ -0,0 +1,194 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// mocking_bit_gen.h +// ----------------------------------------------------------------------------- +// +// This file includes an `absl::MockingBitGen` class to use as a mock within the +// Googletest testing framework. Such a mock is useful to provide deterministic +// values as return values within (otherwise random) Abseil distribution +// functions. Such determinism within a mock is useful within testing frameworks +// to test otherwise indeterminate APIs. +// +// More information about the Googletest testing framework is available at +// https://github.com/google/googletest + +#ifndef ABSL_RANDOM_MOCKING_BIT_GEN_H_ +#define ABSL_RANDOM_MOCKING_BIT_GEN_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/container/flat_hash_map.h" +#include "absl/meta/type_traits.h" +#include "absl/random/distributions.h" +#include "absl/random/internal/distribution_caller.h" +#include "absl/random/internal/mocking_bit_gen_base.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_join.h" +#include "absl/types/span.h" +#include "absl/types/variant.h" +#include "absl/utility/utility.h" + +namespace absl { + +namespace random_internal { + +template +struct MockSingleOverload; + +} // namespace random_internal + +// MockingBitGen +// +// `absl::MockingBitGen` is a mock Uniform Random Bit Generator (URBG) class +// which can act in place of an `absl::BitGen` URBG within tests using the +// Googletest testing framework. +// +// Usage: +// +// Use an `absl::MockingBitGen` along with a mock distribution object (within +// mock_distributions.h) inside Googletest constructs such as ON_CALL(), +// EXPECT_TRUE(), etc. to produce deterministic results conforming to the +// distribution's API contract. +// +// Example: +// +// // Mock a call to an `absl::Bernoulli` distribution using Googletest +// absl::MockingBitGen bitgen; +// +// ON_CALL(absl::MockBernoulli(), Call(bitgen, 0.5)) +// .WillByDefault(testing::Return(true)); +// EXPECT_TRUE(absl::Bernoulli(bitgen, 0.5)); +// +// // Mock a call to an `absl::Uniform` distribution within Googletest +// absl::MockingBitGen bitgen; +// +// ON_CALL(absl::MockUniform(), Call(bitgen, testing::_, testing::_)) +// .WillByDefault([] (int low, int high) { +// return (low + high) / 2; +// }); +// +// EXPECT_EQ(absl::Uniform(gen, 0, 10), 5); +// EXPECT_EQ(absl::Uniform(gen, 30, 40), 35); +// +// At this time, only mock distributions supplied within the Abseil random +// library are officially supported. +// +class MockingBitGen : public absl::random_internal::MockingBitGenBase { + public: + MockingBitGen() {} + + ~MockingBitGen() override; + + private: + template + using MockFnType = + ::testing::MockFunction; + + // MockingBitGen::Register + // + // Register is the main extension point for + // extending the MockingBitGen framework. It provides a mechanism to install a + // mock expectation for the distribution `distr_t` onto the MockingBitGen + // context. + // + // The returned MockFunction<...> type can be used to setup additional + // distribution parameters of the expectation. + template + decltype(std::declval>().gmock_Call( + std::declval()...)) + Register(Ms&&... matchers) { + auto& mock = + mocks_[std::type_index(GetTypeId>())]; + + if (!mock.mock_fn) { + auto* mock_fn = new MockFnType; + mock.mock_fn = mock_fn; + mock.match_impl = &MatchImpl; + deleters_.emplace_back([mock_fn] { delete mock_fn; }); + } + + return static_cast*>(mock.mock_fn) + ->gmock_Call(std::forward(matchers)...); + } + + mutable std::vector> deleters_; + + using match_impl_fn = void (*)(void* mock_fn, void* t_erased_dist_args, + void* t_erased_result); + struct MockData { + void* mock_fn = nullptr; + match_impl_fn match_impl = nullptr; + }; + + mutable absl::flat_hash_map mocks_; + + template + static void MatchImpl(void* mock_fn, void* dist_args, void* result) { + using result_type = typename DistrT::result_type; + *static_cast(result) = absl::apply( + [mock_fn](Args... args) -> result_type { + return (*static_cast*>(mock_fn)) + .Call(std::move(args)...); + }, + *static_cast*>(dist_args)); + } + + // Looks for an appropriate mock - Returns the mocked result if one is found. + // Otherwise, returns a random value generated by the underlying URBG. + bool CallImpl(const std::type_info& key_type, void* dist_args, + void* result) override { + // Trigger a mock, if there exists one that matches `param`. + auto it = mocks_.find(std::type_index(key_type)); + if (it == mocks_.end()) return false; + auto* mock_data = static_cast(&it->second); + mock_data->match_impl(mock_data->mock_fn, dist_args, result); + return true; + } + + template + friend struct ::absl::random_internal::MockSingleOverload; + friend struct ::absl::random_internal::DistributionCaller< + absl::MockingBitGen>; +}; + +// ----------------------------------------------------------------------------- +// Implementation Details Only Below +// ----------------------------------------------------------------------------- + +namespace random_internal { + +template <> +struct DistributionCaller { + template + static typename DistrT::result_type Call(absl::MockingBitGen* gen, + Args&&... args) { + return gen->template Call(std::forward(args)...); + } +}; + +} // namespace random_internal +} // namespace absl + +#endif // ABSL_RANDOM_MOCKING_BIT_GEN_H_ diff --git a/absl/random/mocking_bit_gen_test.cc b/absl/random/mocking_bit_gen_test.cc new file mode 100644 index 00000000000..dcf74fd6db8 --- /dev/null +++ b/absl/random/mocking_bit_gen_test.cc @@ -0,0 +1,347 @@ +// +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#include "absl/random/mocking_bit_gen.h" + +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest-spi.h" +#include "gtest/gtest.h" +#include "absl/random/bit_gen_ref.h" +#include "absl/random/mock_distributions.h" +#include "absl/random/random.h" + +namespace { +using ::testing::Ne; +using ::testing::Return; + +TEST(BasicMocking, AllDistributionsAreOverridable) { + absl::MockingBitGen gen; + + EXPECT_NE(absl::Uniform(gen, 1, 1000000), 20); + EXPECT_CALL(absl::MockUniform(), Call(gen, 1, 1000000)) + .WillOnce(Return(20)); + EXPECT_EQ(absl::Uniform(gen, 1, 1000000), 20); + + EXPECT_NE(absl::Uniform(gen, 0.0, 100.0), 5.0); + EXPECT_CALL(absl::MockUniform(), Call(gen, 0.0, 100.0)) + .WillOnce(Return(5.0)); + EXPECT_EQ(absl::Uniform(gen, 0.0, 100.0), 5.0); + + EXPECT_NE(absl::Exponential(gen, 1.0), 42); + EXPECT_CALL(absl::MockExponential(), Call(gen, 1.0)) + .WillOnce(Return(42)); + EXPECT_EQ(absl::Exponential(gen, 1.0), 42); + + EXPECT_NE(absl::Poisson(gen, 1.0), 500); + EXPECT_CALL(absl::MockPoisson(), Call(gen, 1.0)).WillOnce(Return(500)); + EXPECT_EQ(absl::Poisson(gen, 1.0), 500); + + EXPECT_NE(absl::Bernoulli(gen, 0.000001), true); + EXPECT_CALL(absl::MockBernoulli(), Call(gen, 0.000001)) + .WillOnce(Return(true)); + EXPECT_EQ(absl::Bernoulli(gen, 0.000001), true); + + EXPECT_NE(absl::Zipf(gen, 1000000, 2.0, 1.0), 1221); + EXPECT_CALL(absl::MockZipf(), Call(gen, 1000000, 2.0, 1.0)) + .WillOnce(Return(1221)); + EXPECT_EQ(absl::Zipf(gen, 1000000, 2.0, 1.0), 1221); + + EXPECT_NE(absl::Gaussian(gen, 0.0, 1.0), 0.001); + EXPECT_CALL(absl::MockGaussian(), Call(gen, 0.0, 1.0)) + .WillOnce(Return(0.001)); + EXPECT_EQ(absl::Gaussian(gen, 0.0, 1.0), 0.001); + + EXPECT_NE(absl::LogUniform(gen, 0, 1000000, 2), 2040); + EXPECT_CALL(absl::MockLogUniform(), Call(gen, 0, 1000000, 2)) + .WillOnce(Return(2040)); + EXPECT_EQ(absl::LogUniform(gen, 0, 1000000, 2), 2040); +} + +TEST(BasicMocking, OnDistribution) { + absl::MockingBitGen gen; + + EXPECT_NE(absl::Uniform(gen, 1, 1000000), 20); + ON_CALL(absl::MockUniform(), Call(gen, 1, 1000000)) + .WillByDefault(Return(20)); + EXPECT_EQ(absl::Uniform(gen, 1, 1000000), 20); + + EXPECT_NE(absl::Uniform(gen, 0.0, 100.0), 5.0); + ON_CALL(absl::MockUniform(), Call(gen, 0.0, 100.0)) + .WillByDefault(Return(5.0)); + EXPECT_EQ(absl::Uniform(gen, 0.0, 100.0), 5.0); + + EXPECT_NE(absl::Exponential(gen, 1.0), 42); + ON_CALL(absl::MockExponential(), Call(gen, 1.0)) + .WillByDefault(Return(42)); + EXPECT_EQ(absl::Exponential(gen, 1.0), 42); + + EXPECT_NE(absl::Poisson(gen, 1.0), 500); + ON_CALL(absl::MockPoisson(), Call(gen, 1.0)).WillByDefault(Return(500)); + EXPECT_EQ(absl::Poisson(gen, 1.0), 500); + + EXPECT_NE(absl::Bernoulli(gen, 0.000001), true); + ON_CALL(absl::MockBernoulli(), Call(gen, 0.000001)) + .WillByDefault(Return(true)); + EXPECT_EQ(absl::Bernoulli(gen, 0.000001), true); + + EXPECT_NE(absl::Zipf(gen, 1000000, 2.0, 1.0), 1221); + ON_CALL(absl::MockZipf(), Call(gen, 1000000, 2.0, 1.0)) + .WillByDefault(Return(1221)); + EXPECT_EQ(absl::Zipf(gen, 1000000, 2.0, 1.0), 1221); + + EXPECT_NE(absl::Gaussian(gen, 0.0, 1.0), 0.001); + ON_CALL(absl::MockGaussian(), Call(gen, 0.0, 1.0)) + .WillByDefault(Return(0.001)); + EXPECT_EQ(absl::Gaussian(gen, 0.0, 1.0), 0.001); + + EXPECT_NE(absl::LogUniform(gen, 0, 1000000, 2), 2040); + ON_CALL(absl::MockLogUniform(), Call(gen, 0, 1000000, 2)) + .WillByDefault(Return(2040)); + EXPECT_EQ(absl::LogUniform(gen, 0, 1000000, 2), 2040); +} + +TEST(BasicMocking, GMockMatchers) { + absl::MockingBitGen gen; + + EXPECT_NE(absl::Zipf(gen, 1000000, 2.0, 1.0), 1221); + ON_CALL(absl::MockZipf(), Call(gen, 1000000, 2.0, 1.0)) + .WillByDefault(Return(1221)); + EXPECT_EQ(absl::Zipf(gen, 1000000, 2.0, 1.0), 1221); +} + +TEST(BasicMocking, OverridesWithMultipleGMockExpectations) { + absl::MockingBitGen gen; + + EXPECT_CALL(absl::MockUniform(), Call(gen, 1, 10000)) + .WillOnce(Return(20)) + .WillOnce(Return(40)) + .WillOnce(Return(60)); + EXPECT_EQ(absl::Uniform(gen, 1, 10000), 20); + EXPECT_EQ(absl::Uniform(gen, 1, 10000), 40); + EXPECT_EQ(absl::Uniform(gen, 1, 10000), 60); +} + +TEST(BasicMocking, DefaultArgument) { + absl::MockingBitGen gen; + + ON_CALL(absl::MockExponential(), Call(gen, 1.0)) + .WillByDefault(Return(200)); + + EXPECT_EQ(absl::Exponential(gen), 200); + EXPECT_EQ(absl::Exponential(gen, 1.0), 200); +} + +TEST(BasicMocking, MultipleGenerators) { + auto get_value = [](absl::BitGenRef gen_ref) { + return absl::Uniform(gen_ref, 1, 1000000); + }; + absl::MockingBitGen unmocked_generator; + absl::MockingBitGen mocked_with_3; + absl::MockingBitGen mocked_with_11; + + EXPECT_CALL(absl::MockUniform(), Call(mocked_with_3, 1, 1000000)) + .WillOnce(Return(3)) + .WillRepeatedly(Return(17)); + EXPECT_CALL(absl::MockUniform(), Call(mocked_with_11, 1, 1000000)) + .WillOnce(Return(11)) + .WillRepeatedly(Return(17)); + + // Ensure that unmocked generator generates neither value. + int unmocked_value = get_value(unmocked_generator); + EXPECT_NE(unmocked_value, 3); + EXPECT_NE(unmocked_value, 11); + // Mocked generators should generate their mocked values. + EXPECT_EQ(get_value(mocked_with_3), 3); + EXPECT_EQ(get_value(mocked_with_11), 11); + // Ensure that the mocks have expired. + EXPECT_NE(get_value(mocked_with_3), 3); + EXPECT_NE(get_value(mocked_with_11), 11); +} + +TEST(BasicMocking, MocksNotTrigeredForIncorrectTypes) { + absl::MockingBitGen gen; + EXPECT_CALL(absl::MockUniform(), Call(gen)).WillOnce(Return(42)); + + EXPECT_NE(absl::Uniform(gen), 42); // Not mocked + EXPECT_EQ(absl::Uniform(gen), 42); // Mock triggered +} + +TEST(BasicMocking, FailsOnUnsatisfiedMocks) { + EXPECT_NONFATAL_FAILURE( + []() { + absl::MockingBitGen gen; + EXPECT_CALL(absl::MockExponential(), Call(gen, 1.0)) + .WillOnce(Return(3.0)); + // Does not call absl::Exponential(). + }(), + "unsatisfied and active"); +} + +TEST(OnUniform, RespectsUniformIntervalSemantics) { + absl::MockingBitGen gen; + + EXPECT_CALL(absl::MockUniform(), + Call(absl::IntervalClosed, gen, 1, 1000000)) + .WillOnce(Return(301)); + EXPECT_NE(absl::Uniform(gen, 1, 1000000), 301); // Not mocked + EXPECT_EQ(absl::Uniform(absl::IntervalClosed, gen, 1, 1000000), 301); +} + +TEST(OnUniform, RespectsNoArgUnsignedShorthand) { + absl::MockingBitGen gen; + EXPECT_CALL(absl::MockUniform(), Call(gen)).WillOnce(Return(42)); + EXPECT_EQ(absl::Uniform(gen), 42); +} + +TEST(RepeatedlyModifier, ForceSnakeEyesForManyDice) { + auto roll_some_dice = [](absl::BitGenRef gen_ref) { + std::vector results(16); + for (auto& r : results) { + r = absl::Uniform(absl::IntervalClosed, gen_ref, 1, 6); + } + return results; + }; + std::vector results; + absl::MockingBitGen gen; + + // Without any mocked calls, not all dice roll a "6". + results = roll_some_dice(gen); + EXPECT_LT(std::accumulate(std::begin(results), std::end(results), 0), + results.size() * 6); + + // Verify that we can force all "6"-rolls, with mocking. + ON_CALL(absl::MockUniform(), Call(absl::IntervalClosed, gen, 1, 6)) + .WillByDefault(Return(6)); + results = roll_some_dice(gen); + EXPECT_EQ(std::accumulate(std::begin(results), std::end(results), 0), + results.size() * 6); +} + +TEST(WillOnce, DistinctCounters) { + absl::MockingBitGen gen; + EXPECT_CALL(absl::MockUniform(), Call(gen, 1, 1000000)) + .Times(3) + .WillRepeatedly(Return(0)); + EXPECT_CALL(absl::MockUniform(), Call(gen, 1000001, 2000000)) + .Times(3) + .WillRepeatedly(Return(1)); + EXPECT_EQ(absl::Uniform(gen, 1000001, 2000000), 1); + EXPECT_EQ(absl::Uniform(gen, 1, 1000000), 0); + EXPECT_EQ(absl::Uniform(gen, 1000001, 2000000), 1); + EXPECT_EQ(absl::Uniform(gen, 1, 1000000), 0); + EXPECT_EQ(absl::Uniform(gen, 1000001, 2000000), 1); + EXPECT_EQ(absl::Uniform(gen, 1, 1000000), 0); +} + +TEST(TimesModifier, ModifierSaturatesAndExpires) { + EXPECT_NONFATAL_FAILURE( + []() { + absl::MockingBitGen gen; + EXPECT_CALL(absl::MockUniform(), Call(gen, 1, 1000000)) + .Times(3) + .WillRepeatedly(Return(15)) + .RetiresOnSaturation(); + + EXPECT_EQ(absl::Uniform(gen, 1, 1000000), 15); + EXPECT_EQ(absl::Uniform(gen, 1, 1000000), 15); + EXPECT_EQ(absl::Uniform(gen, 1, 1000000), 15); + // Times(3) has expired - Should get a different value now. + + EXPECT_NE(absl::Uniform(gen, 1, 1000000), 15); + }(), + ""); +} + +TEST(TimesModifier, Times0) { + absl::MockingBitGen gen; + EXPECT_CALL(absl::MockBernoulli(), Call(gen, 0.0)).Times(0); + EXPECT_CALL(absl::MockPoisson(), Call(gen, 1.0)).Times(0); +} + +TEST(AnythingMatcher, MatchesAnyArgument) { + using testing::_; + + { + absl::MockingBitGen gen; + ON_CALL(absl::MockUniform(), Call(absl::IntervalClosed, gen, _, 1000)) + .WillByDefault(Return(11)); + ON_CALL(absl::MockUniform(), + Call(absl::IntervalClosed, gen, _, Ne(1000))) + .WillByDefault(Return(99)); + + EXPECT_EQ(absl::Uniform(absl::IntervalClosed, gen, 10, 1000000), 99); + EXPECT_EQ(absl::Uniform(absl::IntervalClosed, gen, 10, 1000), 11); + } + + { + absl::MockingBitGen gen; + ON_CALL(absl::MockUniform(), Call(gen, 1, _)) + .WillByDefault(Return(25)); + ON_CALL(absl::MockUniform(), Call(gen, Ne(1), _)) + .WillByDefault(Return(99)); + EXPECT_EQ(absl::Uniform(gen, 3, 1000000), 99); + EXPECT_EQ(absl::Uniform(gen, 1, 1000000), 25); + } + + { + absl::MockingBitGen gen; + ON_CALL(absl::MockUniform(), Call(gen, _, _)) + .WillByDefault(Return(145)); + EXPECT_EQ(absl::Uniform(gen, 1, 1000), 145); + EXPECT_EQ(absl::Uniform(gen, 10, 1000), 145); + EXPECT_EQ(absl::Uniform(gen, 100, 1000), 145); + } +} + +TEST(AnythingMatcher, WithWillByDefault) { + using testing::_; + absl::MockingBitGen gen; + std::vector values = {11, 22, 33, 44, 55, 66, 77, 88, 99, 1010}; + + ON_CALL(absl::MockUniform(), Call(gen, 0, _)) + .WillByDefault(Return(0)); + for (int i = 0; i < 100; i++) { + auto& elem = values[absl::Uniform(gen, 0u, values.size())]; + EXPECT_EQ(elem, 11); + } +} + +TEST(BasicMocking, WillByDefaultWithArgs) { + using testing::_; + + absl::MockingBitGen gen; + ON_CALL(absl::MockPoisson(), Call(gen, _)) + .WillByDefault( + [](double lambda) { return static_cast(lambda * 10); }); + EXPECT_EQ(absl::Poisson(gen, 1.7), 17); + EXPECT_EQ(absl::Poisson(gen, 0.03), 0); +} + +TEST(MockingBitGen, InSequenceSucceedsInOrder) { + absl::MockingBitGen gen; + + testing::InSequence seq; + + EXPECT_CALL(absl::MockPoisson(), Call(gen, 1.0)).WillOnce(Return(3)); + EXPECT_CALL(absl::MockPoisson(), Call(gen, 2.0)).WillOnce(Return(4)); + + EXPECT_EQ(absl::Poisson(gen, 1.0), 3); + EXPECT_EQ(absl::Poisson(gen, 2.0), 4); +} + +} // namespace From 16d9fd58a51c6083234e2e9f8f50e49ed5ed02e4 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Thu, 21 Nov 2019 10:30:22 -0800 Subject: [PATCH 0022/3164] Export of internal Abseil changes -- db8dbd0e8a7b0125a4819dfc81c9bd2496849c71 by Abseil Team : Create GetSkipCount() and GetStride() methods and add rounding bias correction. PiperOrigin-RevId: 281780897 GitOrigin-RevId: db8dbd0e8a7b0125a4819dfc81c9bd2496849c71 Change-Id: I56a97288b1cb38a9357c065747f8d9bc4b187fee --- absl/base/internal/exponential_biased.cc | 45 +++++++++- absl/base/internal/exponential_biased.h | 86 +++++++++++++++---- absl/base/internal/exponential_biased_test.cc | 35 +++++++- absl/base/internal/periodic_sampler.cc | 7 +- .../internal/periodic_sampler_benchmark.cc | 7 ++ absl/base/internal/periodic_sampler_test.cc | 12 +-- 6 files changed, 162 insertions(+), 30 deletions(-) diff --git a/absl/base/internal/exponential_biased.cc b/absl/base/internal/exponential_biased.cc index d7ffd184e96..3007f9b46b8 100644 --- a/absl/base/internal/exponential_biased.cc +++ b/absl/base/internal/exponential_biased.cc @@ -16,6 +16,7 @@ #include +#include #include #include #include @@ -26,6 +27,42 @@ namespace absl { namespace base_internal { + +int64_t ExponentialBiased::GetSkipCount(int64_t mean) { + if (ABSL_PREDICT_FALSE(!initialized_)) { + Initialize(); + } + + uint64_t rng = NextRandom(rng_); + rng_ = rng; + + // Take the top 26 bits as the random number + // (This plus the 1<<58 sampling bound give a max possible step of + // 5194297183973780480 bytes.) + // The uint32_t cast is to prevent a (hard-to-reproduce) NAN + // under piii debug for some binaries. + double q = static_cast(rng >> (kPrngNumBits - 26)) + 1.0; + // Put the computed p-value through the CDF of a geometric. + double interval = bias_ + (std::log2(q) - 26) * (-std::log(2.0) * mean); + // Very large values of interval overflow int64_t. To avoid that, we will + // cheat and clamp any huge values to (int64_t max)/2. This is a potential + // source of bias, but the mean would need to be such a large value that it's + // not likely to come up. For example, with a mean of 1e18, the probability of + // hitting this condition is about 1/1000. For a mean of 1e17, standard + // calculators claim that this event won't happen. + if (interval > static_cast(std::numeric_limits::max() / 2)) { + // Assume huge values are bias neutral, retain bias for next call. + return std::numeric_limits::max() / 2; + } + double value = std::round(interval); + bias_ = interval - value; + return value; +} + +int64_t ExponentialBiased::GetStride(int64_t mean) { + return GetSkipCount(mean - 1) + 1; +} + // The algorithm generates a random number between 0 and 1 and applies the // inverse cumulative distribution function for an exponential. Specifically: // Let m be the inverse of the sample period, then the probability @@ -51,7 +88,7 @@ int64_t ExponentialBiased::Get(int64_t mean) { // under piii debug for some binaries. double q = static_cast(rng >> (kPrngNumBits - 26)) + 1.0; // Put the computed p-value through the CDF of a geometric. - double interval = (std::log2(q) - 26) * (-std::log(2.0) * mean); + double interval = bias_ + (std::log2(q) - 26) * (-std::log(2.0) * mean); // Very large values of interval overflow int64_t. To avoid that, we will cheat // and clamp any huge values to (int64_t max)/2. This is a potential source of // bias, but the mean would need to be such a large value that it's not likely @@ -59,10 +96,12 @@ int64_t ExponentialBiased::Get(int64_t mean) { // this condition is about 1/1000. For a mean of 1e17, standard calculators // claim that this event won't happen. if (interval > static_cast(std::numeric_limits::max() / 2)) { + // Assume huge values are bias neutral, retain bias for next call. return std::numeric_limits::max() / 2; } - - return static_cast(interval); + int64_t value = std::max(1, std::round(interval)); + bias_ = interval - value; + return value; } void ExponentialBiased::Initialize() { diff --git a/absl/base/internal/exponential_biased.h b/absl/base/internal/exponential_biased.h index cac2d8ad84f..571505d3267 100644 --- a/absl/base/internal/exponential_biased.h +++ b/absl/base/internal/exponential_biased.h @@ -17,24 +17,56 @@ #include +#include "absl/base/macros.h" + namespace absl { namespace base_internal { // ExponentialBiased provides a small and fast random number generator for a -// rounded exponential distribution. This generator doesn't requires very little -// state doesn't impose synchronization overhead, which makes it useful in some -// specialized scenarios. +// rounded exponential distribution. This generator manages very little state, +// and imposes no synchronization overhead. This makes it useful in specialized +// scenarios requiring minimum overhead, such as stride based periodic sampling. +// +// ExponentialBiased provides two closely related functions, GetSkipCount() and +// GetStride(), both returning a rounded integer defining a number of events +// required before some event with a given mean probability occurs. +// +// The distribution is useful to generate a random wait time or some periodic +// event with a given mean probability. For example, if an action is supposed to +// happen on average once every 'N' events, then we can get a random 'stride' +// counting down how long before the event to happen. For example, if we'd want +// to sample one in every 1000 'Frobber' calls, our code could look like this: +// +// Frobber::Frobber() { +// stride_ = exponential_biased_.GetStride(1000); +// } +// +// void Frobber::Frob(int arg) { +// if (--stride == 0) { +// SampleFrob(arg); +// stride_ = exponential_biased_.GetStride(1000); +// } +// ... +// } +// +// The rounding of the return value creates a bias, especially for smaller means +// where the distribution of the fraction is not evenly distributed. We correct +// this bias by tracking the fraction we rounded up or down on each iteration, +// effectively tracking the distance between the cumulative value, and the +// rounded cumulative value. For example, given a mean of 2: // -// For the generated variable X, X ~ floor(Exponential(1/mean)). The floor -// operation introduces a small amount of bias, but the distribution is useful -// to generate a wait time. That is, if an operation is supposed to happen on -// average to 1/mean events, then the generated variable X will describe how -// many events to skip before performing the operation and computing a new X. +// raw = 1.63076, cumulative = 1.63076, rounded = 2, bias = -0.36923 +// raw = 0.14624, cumulative = 1.77701, rounded = 2, bias = 0.14624 +// raw = 4.93194, cumulative = 6.70895, rounded = 7, bias = -0.06805 +// raw = 0.24206, cumulative = 6.95101, rounded = 7, bias = 0.24206 +// etc... // -// The mathematically precise distribution to use for integer wait times is a -// Geometric distribution, but a Geometric distribution takes slightly more time -// to compute and when the mean is large (say, 100+), the Geometric distribution -// is hard to distinguish from the result of ExponentialBiased. +// Adjusting with rounding bias is relatively trivial: +// +// double value = bias_ + exponential_distribution(mean)(); +// double rounded_value = std::round(value); +// bias_ = value - rounded_value; +// return rounded_value; // // This class is thread-compatible. class ExponentialBiased { @@ -42,9 +74,32 @@ class ExponentialBiased { // The number of bits set by NextRandom. static constexpr int kPrngNumBits = 48; - // Generates the floor of an exponentially distributed random variable by - // rounding the value down to the nearest integer. The result will be in the - // range [0, int64_t max / 2]. + // `GetSkipCount()` returns the number of events to skip before some chosen + // event happens. For example, randomly tossing a coin, we will on average + // throw heads once before we get tails. We can simulate random coin tosses + // using GetSkipCount() as: + // + // ExponentialBiased eb; + // for (...) { + // int number_of_heads_before_tail = eb.GetSkipCount(1); + // for (int flips = 0; flips < number_of_heads_before_tail; ++flips) { + // printf("head..."); + // } + // printf("tail\n"); + // } + // + int64_t GetSkipCount(int64_t mean); + + // GetStride() returns the number of events required for a specific event to + // happen. See the class comments for a usage example. `GetStride()` is + // equivalent to `GetSkipCount(mean - 1) + 1`. When to use `GetStride()` or + // `GetSkipCount()` depends mostly on what best fits the use case. + int64_t GetStride(int64_t mean); + + // Generates a rounded exponentially distributed random variable + // by rounding the value to the nearest integer. + // The result will be in the range [0, int64_t max / 2]. + ABSL_DEPRECATED("Use GetSkipCount() or GetStride() instead") int64_t Get(int64_t mean); // Computes a random number in the range [0, 1<<(kPrngNumBits+1) - 1] @@ -56,6 +111,7 @@ class ExponentialBiased { void Initialize(); uint64_t rng_{0}; + double bias_{0}; bool initialized_{false}; }; diff --git a/absl/base/internal/exponential_biased_test.cc b/absl/base/internal/exponential_biased_test.cc index 09b511d14e7..af003239f8d 100644 --- a/absl/base/internal/exponential_biased_test.cc +++ b/absl/base/internal/exponential_biased_test.cc @@ -113,6 +113,35 @@ double AndersonDarlingTest(const std::vector& random_sample) { return p; } +TEST(ExponentialBiasedTest, CoinTossDemoWithGetSkipCount) { + ExponentialBiased eb; + for (int runs = 0; runs < 10; ++runs) { + for (int flips = eb.GetSkipCount(1); flips > 0; --flips) { + printf("head..."); + } + printf("tail\n"); + } + int heads = 0; + for (int i = 0; i < 10000000; i += 1 + eb.GetSkipCount(1)) { + ++heads; + } + printf("Heads = %d (%f%%)\n", heads, 100.0 * heads / 10000000); +} + +TEST(ExponentialBiasedTest, SampleDemoWithStride) { + ExponentialBiased eb; + int stride = eb.GetStride(10); + int samples = 0; + for (int i = 0; i < 10000000; ++i) { + if (--stride == 0) { + ++samples; + stride = eb.GetStride(10); + } + } + printf("Samples = %d (%f%%)\n", samples, 100.0 * samples / 10000000); +} + + // Testing that NextRandom generates uniform random numbers. Applies the // Anderson-Darling test for uniformity TEST(ExponentialBiasedTest, TestNextRandom) { @@ -153,15 +182,15 @@ TEST(ExponentialBiasedTest, TestNextRandom) { // variable. TEST(ExponentialBiasedTest, InitializationModes) { ABSL_CONST_INIT static ExponentialBiased eb_static; - EXPECT_THAT(eb_static.Get(2), Ge(0)); + EXPECT_THAT(eb_static.GetSkipCount(2), Ge(0)); #if ABSL_HAVE_THREAD_LOCAL thread_local ExponentialBiased eb_thread; - EXPECT_THAT(eb_thread.Get(2), Ge(0)); + EXPECT_THAT(eb_thread.GetSkipCount(2), Ge(0)); #endif ExponentialBiased eb_stack; - EXPECT_THAT(eb_stack.Get(2), Ge(0)); + EXPECT_THAT(eb_stack.GetSkipCount(2), Ge(0)); } } // namespace base_internal diff --git a/absl/base/internal/periodic_sampler.cc b/absl/base/internal/periodic_sampler.cc index 69656c8a0fb..87c3c85ae3e 100644 --- a/absl/base/internal/periodic_sampler.cc +++ b/absl/base/internal/periodic_sampler.cc @@ -22,7 +22,7 @@ namespace absl { namespace base_internal { int64_t PeriodicSamplerBase::GetExponentialBiased(int period) noexcept { - return rng_.Get(period); + return rng_.GetStride(period); } bool PeriodicSamplerBase::SubtleConfirmSample() noexcept { @@ -36,13 +36,14 @@ bool PeriodicSamplerBase::SubtleConfirmSample() noexcept { // Check if this is the first call to Sample() if (ABSL_PREDICT_FALSE(stride_ == 1)) { - stride_ = static_cast(-1 - GetExponentialBiased(current_period)); + stride_ = static_cast(-GetExponentialBiased(current_period)); if (static_cast(stride_) < -1) { ++stride_; return false; } } - stride_ = static_cast(-1 - GetExponentialBiased(current_period)); + + stride_ = static_cast(-GetExponentialBiased(current_period)); return true; } diff --git a/absl/base/internal/periodic_sampler_benchmark.cc b/absl/base/internal/periodic_sampler_benchmark.cc index f87cef73dc1..03759632177 100644 --- a/absl/base/internal/periodic_sampler_benchmark.cc +++ b/absl/base/internal/periodic_sampler_benchmark.cc @@ -37,6 +37,13 @@ void BM_SampleMinunumInlined(Sampler* sampler, benchmark::State& state) { } } +void BM_PeriodicSampler_TinySample(benchmark::State& state) { + struct Tag {}; + PeriodicSampler sampler; + BM_Sample(&sampler, state); +} +BENCHMARK(BM_PeriodicSampler_TinySample); + void BM_PeriodicSampler_ShortSample(benchmark::State& state) { struct Tag {}; PeriodicSampler sampler; diff --git a/absl/base/internal/periodic_sampler_test.cc b/absl/base/internal/periodic_sampler_test.cc index 1ba61377a0f..29f4d24d632 100644 --- a/absl/base/internal/periodic_sampler_test.cc +++ b/absl/base/internal/periodic_sampler_test.cc @@ -42,9 +42,9 @@ TEST(PeriodicSamplerBaseTest, Sample) { EXPECT_CALL(sampler, period()).Times(3).WillRepeatedly(Return(16)); EXPECT_CALL(sampler, GetExponentialBiased(16)) - .WillOnce(Return(1)) .WillOnce(Return(2)) - .WillOnce(Return(3)); + .WillOnce(Return(3)) + .WillOnce(Return(4)); EXPECT_FALSE(sampler.Sample()); EXPECT_TRUE(sampler.Sample()); @@ -63,9 +63,9 @@ TEST(PeriodicSamplerBaseTest, ImmediatelySample) { EXPECT_CALL(sampler, period()).Times(2).WillRepeatedly(Return(16)); EXPECT_CALL(sampler, GetExponentialBiased(16)) - .WillOnce(Return(0)) .WillOnce(Return(1)) - .WillOnce(Return(2)); + .WillOnce(Return(2)) + .WillOnce(Return(3)); EXPECT_TRUE(sampler.Sample()); @@ -100,7 +100,7 @@ TEST(PeriodicSamplerBaseTest, Disable) { StrictMock sampler; EXPECT_CALL(sampler, period()).WillOnce(Return(16)); - EXPECT_CALL(sampler, GetExponentialBiased(16)).WillOnce(Return(2)); + EXPECT_CALL(sampler, GetExponentialBiased(16)).WillOnce(Return(3)); EXPECT_FALSE(sampler.Sample()); EXPECT_FALSE(sampler.Sample()); @@ -119,7 +119,7 @@ TEST(PeriodicSamplerBaseTest, Enable) { EXPECT_CALL(sampler, period()).Times(2).WillRepeatedly(Return(16)); EXPECT_CALL(sampler, GetExponentialBiased(16)) .Times(2) - .WillRepeatedly(Return(2)); + .WillRepeatedly(Return(3)); EXPECT_FALSE(sampler.Sample()); EXPECT_FALSE(sampler.Sample()); From 7f4fe64af80fe3c84db8ea938276c3690573c45e Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Mon, 25 Nov 2019 10:40:20 -0800 Subject: [PATCH 0023/3164] Export of internal Abseil changes -- 44efc1bb0e0a47eabf0569eaab81c66710d5b9c3 by Mark Barolak : Update "strings::Substitute" to "absl::Substitute" in the absl::Substitute error messages. PiperOrigin-RevId: 282388042 -- 9ec7e9385f5469473f76857dc5b067d869bbc65b by Abseil Team : Remove deprecated ExponentialBiased::Get() PiperOrigin-RevId: 282045123 GitOrigin-RevId: 44efc1bb0e0a47eabf0569eaab81c66710d5b9c3 Change-Id: I915bf0ff5fa7ac2bd5f9fb653d1fbd9ece6af9fc --- absl/base/internal/exponential_biased.cc | 52 ++++--------------- absl/base/internal/exponential_biased.h | 6 --- absl/container/internal/hashtablez_sampler.cc | 6 +-- absl/strings/substitute.cc | 6 +-- absl/strings/substitute_test.cc | 6 +-- 5 files changed, 18 insertions(+), 58 deletions(-) diff --git a/absl/base/internal/exponential_biased.cc b/absl/base/internal/exponential_biased.cc index 3007f9b46b8..7786c303cdd 100644 --- a/absl/base/internal/exponential_biased.cc +++ b/absl/base/internal/exponential_biased.cc @@ -27,7 +27,16 @@ namespace absl { namespace base_internal { - +// The algorithm generates a random number between 0 and 1 and applies the +// inverse cumulative distribution function for an exponential. Specifically: +// Let m be the inverse of the sample period, then the probability +// distribution function is m*exp(-mx) so the CDF is +// p = 1 - exp(-mx), so +// q = 1 - p = exp(-mx) +// log_e(q) = -mx +// -log_e(q)/m = x +// log_2(q) * (-log_e(2) * 1/m) = x +// In the code, q is actually in the range 1 to 2**26, hence the -26 below int64_t ExponentialBiased::GetSkipCount(int64_t mean) { if (ABSL_PREDICT_FALSE(!initialized_)) { Initialize(); @@ -63,47 +72,6 @@ int64_t ExponentialBiased::GetStride(int64_t mean) { return GetSkipCount(mean - 1) + 1; } -// The algorithm generates a random number between 0 and 1 and applies the -// inverse cumulative distribution function for an exponential. Specifically: -// Let m be the inverse of the sample period, then the probability -// distribution function is m*exp(-mx) so the CDF is -// p = 1 - exp(-mx), so -// q = 1 - p = exp(-mx) -// log_e(q) = -mx -// -log_e(q)/m = x -// log_2(q) * (-log_e(2) * 1/m) = x -// In the code, q is actually in the range 1 to 2**26, hence the -26 below -int64_t ExponentialBiased::Get(int64_t mean) { - if (ABSL_PREDICT_FALSE(!initialized_)) { - Initialize(); - } - - uint64_t rng = NextRandom(rng_); - rng_ = rng; - - // Take the top 26 bits as the random number - // (This plus the 1<<58 sampling bound give a max possible step of - // 5194297183973780480 bytes.) - // The uint32_t cast is to prevent a (hard-to-reproduce) NAN - // under piii debug for some binaries. - double q = static_cast(rng >> (kPrngNumBits - 26)) + 1.0; - // Put the computed p-value through the CDF of a geometric. - double interval = bias_ + (std::log2(q) - 26) * (-std::log(2.0) * mean); - // Very large values of interval overflow int64_t. To avoid that, we will cheat - // and clamp any huge values to (int64_t max)/2. This is a potential source of - // bias, but the mean would need to be such a large value that it's not likely - // to come up. For example, with a mean of 1e18, the probability of hitting - // this condition is about 1/1000. For a mean of 1e17, standard calculators - // claim that this event won't happen. - if (interval > static_cast(std::numeric_limits::max() / 2)) { - // Assume huge values are bias neutral, retain bias for next call. - return std::numeric_limits::max() / 2; - } - int64_t value = std::max(1, std::round(interval)); - bias_ = interval - value; - return value; -} - void ExponentialBiased::Initialize() { // We don't get well distributed numbers from `this` so we call NextRandom() a // bunch to mush the bits around. We use a global_rand to handle the case diff --git a/absl/base/internal/exponential_biased.h b/absl/base/internal/exponential_biased.h index 571505d3267..6701e695ef0 100644 --- a/absl/base/internal/exponential_biased.h +++ b/absl/base/internal/exponential_biased.h @@ -96,12 +96,6 @@ class ExponentialBiased { // `GetSkipCount()` depends mostly on what best fits the use case. int64_t GetStride(int64_t mean); - // Generates a rounded exponentially distributed random variable - // by rounding the value to the nearest integer. - // The result will be in the range [0, int64_t max / 2]. - ABSL_DEPRECATED("Use GetSkipCount() or GetStride() instead") - int64_t Get(int64_t mean); - // Computes a random number in the range [0, 1<<(kPrngNumBits+1) - 1] // // This is public to enable testing. diff --git a/absl/container/internal/hashtablez_sampler.cc b/absl/container/internal/hashtablez_sampler.cc index 4aec0a07ffb..6deeca44d97 100644 --- a/absl/container/internal/hashtablez_sampler.cc +++ b/absl/container/internal/hashtablez_sampler.cc @@ -196,12 +196,10 @@ HashtablezInfo* SampleSlow(int64_t* next_sample) { return nullptr; #else bool first = *next_sample < 0; - *next_sample = g_exponential_biased_generator.Get( + *next_sample = g_exponential_biased_generator.GetStride( g_hashtablez_sample_parameter.load(std::memory_order_relaxed)); // Small values of interval are equivalent to just sampling next time. - if (*next_sample < 1) { - *next_sample = 1; - } + ABSL_ASSERT(*next_sample >= 1); // g_hashtablez_enabled can be dynamically flipped, we need to set a threshold // low enough that we will start sampling in a reasonable time, so we just use diff --git a/absl/strings/substitute.cc b/absl/strings/substitute.cc index 36dbfe7d42f..5d28c5286cf 100644 --- a/absl/strings/substitute.cc +++ b/absl/strings/substitute.cc @@ -35,7 +35,7 @@ void SubstituteAndAppendArray(std::string* output, absl::string_view format, if (i + 1 >= format.size()) { #ifndef NDEBUG ABSL_RAW_LOG(FATAL, - "Invalid strings::Substitute() format std::string: \"%s\".", + "Invalid absl::Substitute() format std::string: \"%s\".", absl::CEscape(format).c_str()); #endif return; @@ -45,7 +45,7 @@ void SubstituteAndAppendArray(std::string* output, absl::string_view format, #ifndef NDEBUG ABSL_RAW_LOG( FATAL, - "Invalid strings::Substitute() format std::string: asked for \"$" + "Invalid absl::Substitute() format std::string: asked for \"$" "%d\", but only %d args were given. Full format std::string was: " "\"%s\".", index, static_cast(num_args), absl::CEscape(format).c_str()); @@ -60,7 +60,7 @@ void SubstituteAndAppendArray(std::string* output, absl::string_view format, } else { #ifndef NDEBUG ABSL_RAW_LOG(FATAL, - "Invalid strings::Substitute() format std::string: \"%s\".", + "Invalid absl::Substitute() format std::string: \"%s\".", absl::CEscape(format).c_str()); #endif return; diff --git a/absl/strings/substitute_test.cc b/absl/strings/substitute_test.cc index e27abb17727..b005f0f47ca 100644 --- a/absl/strings/substitute_test.cc +++ b/absl/strings/substitute_test.cc @@ -189,14 +189,14 @@ TEST(SubstituteTest, VectorBoolRef) { TEST(SubstituteDeathTest, SubstituteDeath) { EXPECT_DEBUG_DEATH( static_cast(absl::Substitute(absl::string_view("-$2"), "a", "b")), - "Invalid strings::Substitute\\(\\) format std::string: asked for \"\\$2\", " + "Invalid absl::Substitute\\(\\) format std::string: asked for \"\\$2\", " "but only 2 args were given."); EXPECT_DEBUG_DEATH( static_cast(absl::Substitute("-$z-")), - "Invalid strings::Substitute\\(\\) format std::string: \"-\\$z-\""); + "Invalid absl::Substitute\\(\\) format std::string: \"-\\$z-\""); EXPECT_DEBUG_DEATH( static_cast(absl::Substitute("-$")), - "Invalid strings::Substitute\\(\\) format std::string: \"-\\$\""); + "Invalid absl::Substitute\\(\\) format std::string: \"-\\$\""); } #endif // GTEST_HAS_DEATH_TEST From 0514227d2547793b23e209809276375e41c76617 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Tue, 26 Nov 2019 09:00:19 -0800 Subject: [PATCH 0024/3164] Export of internal Abseil changes -- 2ba0e41a21fbdab36b2f4f3b0dd4b112bd788604 by Derek Mauro : Remove the include of , which is missing on some versions of MinGW. DWORD is easily replaced by uint32_t. PiperOrigin-RevId: 282576177 -- 238fd41114b3e83fcb91d2afe1e6dcce7cfd53b0 by Samuel Benzaquen : Remove assertion in erase(iterator) that tries to use the comparator. Add missing this-> qualifier. Fix bug where node elements are not being destroyed properly. PiperOrigin-RevId: 282427096 -- 6b9446e3b38ed97451c010933e86a572ab659ab2 by Derek Mauro : Improves/fixes feature detection in thread_identity Only use ABSL_PER_THREAD_TLS_KEYWORD when it is supported (previously on some platforms it evaluated to nothing, which completely breaks everything), but prefer it to thread_local since benchmarks indicate it is slightly faster in this critical code path. Disable the calls to pthread_sigmask on MinGW where it is not supported. PiperOrigin-RevId: 282425291 GitOrigin-RevId: 2ba0e41a21fbdab36b2f4f3b0dd4b112bd788604 Change-Id: I34073ecbb4a43ad71f54161c136d88fc728888f1 --- absl/base/internal/sysinfo.cc | 2 +- absl/base/internal/sysinfo.h | 11 +-- absl/base/internal/thread_identity.cc | 13 +++- absl/base/internal/thread_identity.h | 9 ++- absl/container/btree_test.cc | 90 ++++++++++++++++++----- absl/container/internal/btree.h | 1 - absl/container/internal/btree_container.h | 36 +++++---- absl/container/internal/common.h | 5 ++ 8 files changed, 119 insertions(+), 48 deletions(-) diff --git a/absl/base/internal/sysinfo.cc b/absl/base/internal/sysinfo.cc index 4dd3adda46e..93039bf23f1 100644 --- a/absl/base/internal/sysinfo.cc +++ b/absl/base/internal/sysinfo.cc @@ -277,7 +277,7 @@ double NominalCPUFrequency() { #if defined(_WIN32) pid_t GetTID() { - return GetCurrentThreadId(); + return pid_t{GetCurrentThreadId()}; } #elif defined(__linux__) diff --git a/absl/base/internal/sysinfo.h b/absl/base/internal/sysinfo.h index b864a59741f..93356d80d1c 100644 --- a/absl/base/internal/sysinfo.h +++ b/absl/base/internal/sysinfo.h @@ -26,10 +26,10 @@ #ifndef _WIN32 #include -#else -#include #endif +#include + #include "absl/base/port.h" namespace absl { @@ -51,9 +51,10 @@ int NumCPUs(); // On Linux, you may send a signal to the resulting ID with kill(). However, // it is recommended for portability that you use pthread_kill() instead. #ifdef _WIN32 -// On Windows, process id and thread id are of the same type according to -// the return types of GetProcessId() and GetThreadId() are both DWORD. -using pid_t = DWORD; +// On Windows, process id and thread id are of the same type according to the +// return types of GetProcessId() and GetThreadId() are both DWORD, an unsigned +// 32-bit type. +using pid_t = uint32_t; #endif pid_t GetTID(); diff --git a/absl/base/internal/thread_identity.cc b/absl/base/internal/thread_identity.cc index 91273a6b1a1..0ea159c5862 100644 --- a/absl/base/internal/thread_identity.cc +++ b/absl/base/internal/thread_identity.cc @@ -55,7 +55,12 @@ void AllocateThreadIdentityKey(ThreadIdentityReclaimerFunction reclaimer) { #ifdef __GNUC__ __attribute__((visibility("protected"))) #endif // __GNUC__ - ABSL_PER_THREAD_TLS_KEYWORD ThreadIdentity* thread_identity_ptr; +#if ABSL_PER_THREAD_TLS +// Prefer __thread to thread_local as benchmarks indicate it is a bit faster. +ABSL_PER_THREAD_TLS_KEYWORD ThreadIdentity* thread_identity_ptr = nullptr; +#elif defined(ABSL_HAVE_THREAD_LOCAL) +thread_local ThreadIdentity* thread_identity_ptr = nullptr; +#endif // ABSL_PER_THREAD_TLS #endif // TLS or CPP11 void SetCurrentThreadIdentity( @@ -69,8 +74,8 @@ void SetCurrentThreadIdentity( absl::call_once(init_thread_identity_key_once, AllocateThreadIdentityKey, reclaimer); -#ifdef __EMSCRIPTEN__ - // Emscripten PThread implementation does not support signals. +#if defined(__EMSCRIPTEN__) || defined(__MINGW32__) + // Emscripten and MinGW pthread implementations does not support signals. // See https://kripken.github.io/emscripten-site/docs/porting/pthreads.html // for more information. pthread_setspecific(thread_identity_pthread_key, @@ -89,7 +94,7 @@ void SetCurrentThreadIdentity( pthread_setspecific(thread_identity_pthread_key, reinterpret_cast(identity)); pthread_sigmask(SIG_SETMASK, &curr_signals, nullptr); -#endif // !__EMSCRIPTEN__ +#endif // !__EMSCRIPTEN__ && !__MINGW32__ #elif ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_TLS // NOTE: Not async-safe. But can be open-coded. diff --git a/absl/base/internal/thread_identity.h b/absl/base/internal/thread_identity.h index b34674a62d4..7cbce9cc8e0 100644 --- a/absl/base/internal/thread_identity.h +++ b/absl/base/internal/thread_identity.h @@ -224,7 +224,14 @@ void ClearCurrentThreadIdentity(); #if ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_TLS || \ ABSL_THREAD_IDENTITY_MODE == ABSL_THREAD_IDENTITY_MODE_USE_CPP11 -extern ABSL_PER_THREAD_TLS_KEYWORD ThreadIdentity* thread_identity_ptr; +#if ABSL_PER_THREAD_TLS +ABSL_CONST_INIT extern ABSL_PER_THREAD_TLS_KEYWORD ThreadIdentity* + thread_identity_ptr; +#elif defined(ABSL_HAVE_THREAD_LOCAL) +ABSL_CONST_INIT extern thread_local ThreadIdentity* thread_identity_ptr; +#else +#error Thread-local storage not detected on this platform +#endif inline ThreadIdentity* CurrentThreadIdentityIfPresent() { return thread_identity_ptr; diff --git a/absl/container/btree_test.cc b/absl/container/btree_test.cc index 48f70cb0a5c..ea73f032ed6 100644 --- a/absl/container/btree_test.cc +++ b/absl/container/btree_test.cc @@ -45,6 +45,7 @@ namespace absl { namespace container_internal { namespace { +using ::absl::test_internal::CopyableMovableInstance; using ::absl::test_internal::InstanceTracker; using ::absl::test_internal::MovableOnlyInstance; using ::testing::ElementsAre; @@ -1823,25 +1824,80 @@ TEST(Btree, ExtractAndInsertNodeHandleSet) { EXPECT_EQ(res.node.value(), 3); } -struct Deref { - bool operator()(const std::unique_ptr &lhs, - const std::unique_ptr &rhs) const { - return *lhs < *rhs; +template +void TestExtractWithTrackingForSet() { + InstanceTracker tracker; + { + Set s; + // Add enough elements to make sure we test internal nodes too. + const size_t kSize = 1000; + while (s.size() < kSize) { + s.insert(MovableOnlyInstance(s.size())); + } + for (int i = 0; i < kSize; ++i) { + // Extract with key + auto nh = s.extract(MovableOnlyInstance(i)); + EXPECT_EQ(s.size(), kSize - 1); + EXPECT_EQ(nh.value().value(), i); + // Insert with node + s.insert(std::move(nh)); + EXPECT_EQ(s.size(), kSize); + + // Extract with iterator + auto it = s.find(MovableOnlyInstance(i)); + nh = s.extract(it); + EXPECT_EQ(s.size(), kSize - 1); + EXPECT_EQ(nh.value().value(), i); + // Insert with node and hint + s.insert(s.begin(), std::move(nh)); + EXPECT_EQ(s.size(), kSize); + } } -}; + EXPECT_EQ(0, tracker.instances()); +} + +template +void TestExtractWithTrackingForMap() { + InstanceTracker tracker; + { + Map m; + // Add enough elements to make sure we test internal nodes too. + const size_t kSize = 1000; + while (m.size() < kSize) { + m.insert( + {CopyableMovableInstance(m.size()), MovableOnlyInstance(m.size())}); + } + for (int i = 0; i < kSize; ++i) { + // Extract with key + auto nh = m.extract(CopyableMovableInstance(i)); + EXPECT_EQ(m.size(), kSize - 1); + EXPECT_EQ(nh.key().value(), i); + EXPECT_EQ(nh.mapped().value(), i); + // Insert with node + m.insert(std::move(nh)); + EXPECT_EQ(m.size(), kSize); + + // Extract with iterator + auto it = m.find(CopyableMovableInstance(i)); + nh = m.extract(it); + EXPECT_EQ(m.size(), kSize - 1); + EXPECT_EQ(nh.key().value(), i); + EXPECT_EQ(nh.mapped().value(), i); + // Insert with node and hint + m.insert(m.begin(), std::move(nh)); + EXPECT_EQ(m.size(), kSize); + } + } + EXPECT_EQ(0, tracker.instances()); +} -TEST(Btree, ExtractWithUniquePtr) { - absl::btree_set, Deref> s; - s.insert(absl::make_unique(1)); - s.insert(absl::make_unique(2)); - s.insert(absl::make_unique(3)); - s.insert(absl::make_unique(4)); - s.insert(absl::make_unique(5)); - auto nh = s.extract(s.find(absl::make_unique(3))); - EXPECT_EQ(s.size(), 4); - EXPECT_EQ(*nh.value(), 3); - s.insert(std::move(nh)); - EXPECT_EQ(s.size(), 5); +TEST(Btree, ExtractTracking) { + TestExtractWithTrackingForSet>(); + TestExtractWithTrackingForSet>(); + TestExtractWithTrackingForMap< + absl::btree_map>(); + TestExtractWithTrackingForMap< + absl::btree_multimap>(); } TEST(Btree, ExtractAndInsertNodeHandleMultiSet) { diff --git a/absl/container/internal/btree.h b/absl/container/internal/btree.h index cf7ef02cccc..40217dd5f0a 100644 --- a/absl/container/internal/btree.h +++ b/absl/container/internal/btree.h @@ -2031,7 +2031,6 @@ auto btree

::erase(iterator iter) -> iterator { iterator internal_iter(iter); --iter; assert(iter.node->leaf()); - assert(!compare_keys(internal_iter.key(), iter.key())); params_type::move(mutable_allocator(), iter.node->slot(iter.position), internal_iter.node->slot(internal_iter.position)); internal_delete = true; diff --git a/absl/container/internal/btree_container.h b/absl/container/internal/btree_container.h index 726861d5bbb..774412d9159 100644 --- a/absl/container/internal/btree_container.h +++ b/absl/container/internal/btree_container.h @@ -296,9 +296,10 @@ class btree_set_container : public btree_container { insert_return_type insert(node_type &&node) { if (!node) return {this->end(), false, node_type()}; std::pair res = - insert(std::move(params_type::element(CommonAccess::GetSlot(node)))); + this->tree_.insert_unique(params_type::key(CommonAccess::GetSlot(node)), + CommonAccess::GetSlot(node)); if (res.second) { - CommonAccess::Reset(&node); + CommonAccess::Destroy(&node); return {res.first, true, node_type()}; } else { return {res.first, false, std::move(node)}; @@ -308,8 +309,8 @@ class btree_set_container : public btree_container { if (!node) return this->end(); std::pair res = this->tree_.insert_hint_unique( iterator(hint), params_type::key(CommonAccess::GetSlot(node)), - std::move(params_type::element(CommonAccess::GetSlot(node)))); - if (res.second) CommonAccess::Reset(&node); + CommonAccess::GetSlot(node)); + if (res.second) CommonAccess::Destroy(&node); return res.first; } @@ -323,7 +324,7 @@ class btree_set_container : public btree_container { // Node extraction routines. template node_type extract(const key_arg &key) { - auto it = find(key); + auto it = this->find(key); return it == this->end() ? node_type() : extract(it); } using super_type::extract; @@ -523,24 +524,21 @@ class btree_multiset_container : public btree_container { return this->tree_.insert_hint_multi( iterator(position), init_type(std::forward(args)...)); } - - private: - template - iterator insert_node_helper(node_type &&node, Args &&... args) { + iterator insert(node_type &&node) { if (!node) return this->end(); iterator res = - insert(std::forward(args)..., - std::move(params_type::element(CommonAccess::GetSlot(node)))); - CommonAccess::Reset(&node); + this->tree_.insert_multi(params_type::key(CommonAccess::GetSlot(node)), + CommonAccess::GetSlot(node)); + CommonAccess::Destroy(&node); return res; } - - public: - iterator insert(node_type &&node) { - return insert_node_helper(std::move(node)); - } iterator insert(const_iterator hint, node_type &&node) { - return insert_node_helper(std::move(node), hint); + if (!node) return this->end(); + iterator res = this->tree_.insert_hint_multi( + iterator(hint), + std::move(params_type::element(CommonAccess::GetSlot(node)))); + CommonAccess::Destroy(&node); + return res; } // Deletion routines. @@ -553,7 +551,7 @@ class btree_multiset_container : public btree_container { // Node extraction routines. template node_type extract(const key_arg &key) { - auto it = find(key); + auto it = this->find(key); return it == this->end() ? node_type() : extract(it); } using super_type::extract; diff --git a/absl/container/internal/common.h b/absl/container/internal/common.h index 4bd5d469746..cc7633dcfa1 100644 --- a/absl/container/internal/common.h +++ b/absl/container/internal/common.h @@ -166,6 +166,11 @@ struct CommonAccess { return node.slot(); } + template + static void Destroy(Node* node) { + node->destroy(); + } + template static void Reset(Node* node) { node->reset(); From a4b757b5d42694306a9de853cee0a5fba9c7bbe9 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Mon, 2 Dec 2019 10:41:53 -0800 Subject: [PATCH 0025/3164] Export of internal Abseil changes -- 693f81830b9f9cc8b24a1f38492b8dfcdd1d0e24 by Abseil Team : Check that absl::int128 works as a std::chrono::duration::rep. In particular, validate that ... std::chrono::time_point> is a superset (range and resolution) of absl::Time. PiperOrigin-RevId: 283370280 -- df6073b686bd44223c6f9070fcceec918c728871 by Gennadiy Rozental : Changes thread annotations to use DataGuard() function instead of a specific Mutex. Remove unused declaration of InvokeCallback. PiperOrigin-RevId: 283361188 -- b49eb2dd2ee1a0b4c8a7bb1a94e368b81ce5f861 by Abseil Team : Rewrite GetNominalCPUFrequency to use advapi32 instead of shlwapi Using shlwapi.dll means that gdi32.dll is loaded which then makes process destruction more expensive, which is unacceptable for some uses. There may be other places that pull in gdi32.dll - this just fixes the one. PiperOrigin-RevId: 282960698 -- b5508afec5099a0fdbb55e39a7cd2993259ed860 by Abseil Team : Small typo fix in comments: initiazliation -> initialization PiperOrigin-RevId: 282891800 -- 4319cc419584e91ee74f6ae1a32d88a412fc5c01 by Abseil Team : Update c_find_first_of() comment to remove the mention of an ordered container. PiperOrigin-RevId: 282836540 -- 5fcabc0a834dff39a505d5a5fc5403ddeb96028e by Derek Mauro : Fix NaCl build, where format checking is broken PiperOrigin-RevId: 282826202 -- aaf9ad3274c056a2f68e9b8ccada45c9802e2f1e by Derek Mauro : Fix more -Wundef warnings PiperOrigin-RevId: 282799820 -- 1fb06150a70ffe98bf4b2d42b2a39d083bf44f8c by Derek Mauro : Release support for additional platforms PiperOrigin-RevId: 282793384 -- fa947fc28624a316fa872d7045b3838b88a0d69b by Derek Mauro : Cleanup inconsistent usage of __has_attribute PiperOrigin-RevId: 282793296 -- 990030ad282263d6303c83b780a55fdec8e90d43 by Gennadiy Rozental : Eliminate the pointer in absl::Flag, which points to n space where we were storing flag's default value. We also eliminate additional (now unnecessary) allocation for flag's default value. Instead we'll initialize the flags value directly from the value specified in ABSL_FLAG. If the default value is updated via the call to SetCommandLineOptionWithMode we are replacing pointer to initialization routine to pointer to new default value. PiperOrigin-RevId: 282637616 GitOrigin-RevId: 693f81830b9f9cc8b24a1f38492b8dfcdd1d0e24 Change-Id: I6f2edd8ef844de09aa2c182a7ca3133a22364792 --- absl/algorithm/container.h | 3 +- absl/base/BUILD.bazel | 2 +- absl/base/CMakeLists.txt | 2 +- absl/base/internal/atomic_hook.h | 2 +- absl/base/internal/raw_logging.cc | 4 +- absl/base/internal/sysinfo.cc | 26 ++++-- absl/base/macros.h | 10 +- absl/flags/flag.h | 8 +- absl/flags/internal/flag.cc | 71 +++++++++------ absl/flags/internal/flag.h | 91 ++++++++++++------- absl/random/internal/BUILD.bazel | 2 + absl/random/internal/nanobenchmark.cc | 10 +- absl/random/internal/randen_slow.cc | 10 +- absl/random/internal/seed_material.cc | 12 +++ absl/strings/internal/str_format/bind.h | 2 +- absl/strings/internal/str_format/checker.h | 9 +- .../internal/str_format/checker_test.cc | 2 +- absl/strings/internal/str_format/parser.h | 2 +- absl/time/BUILD.bazel | 1 + absl/time/time_test.cc | 45 +++++++++ 20 files changed, 200 insertions(+), 114 deletions(-) diff --git a/absl/algorithm/container.h b/absl/algorithm/container.h index adcea8a7999..5dae9fd6a33 100644 --- a/absl/algorithm/container.h +++ b/absl/algorithm/container.h @@ -268,7 +268,8 @@ container_algorithm_internal::ContainerIter c_find_end( // c_find_first_of() // // Container-based version of the `std::find_first_of()` function to -// find the first elements in an ordered set within a container. +// find the first element within the container that is also within the options +// container. template container_algorithm_internal::ContainerIter c_find_first_of(C1& container, C2& options) { diff --git a/absl/base/BUILD.bazel b/absl/base/BUILD.bazel index f6b210f41e3..f8eb8aeb3b9 100644 --- a/absl/base/BUILD.bazel +++ b/absl/base/BUILD.bazel @@ -193,7 +193,7 @@ cc_library( copts = ABSL_DEFAULT_COPTS, linkopts = select({ "//absl:windows": [ - "-DEFAULTLIB:shlwapi.lib", + "-DEFAULTLIB:advapi32.lib", ], "//conditions:default": ["-pthread"], }) + ABSL_DEFAULT_LINKOPTS, diff --git a/absl/base/CMakeLists.txt b/absl/base/CMakeLists.txt index 9b0174d80ef..c521f32d1ca 100644 --- a/absl/base/CMakeLists.txt +++ b/absl/base/CMakeLists.txt @@ -176,7 +176,7 @@ absl_cc_library( LINKOPTS ${ABSL_DEFAULT_LINKOPTS} $<$:${LIBRT}> - $<$:"shlwapi"> + $<$:"advapi32"> DEPS absl::atomic_hook absl::base_internal diff --git a/absl/base/internal/atomic_hook.h b/absl/base/internal/atomic_hook.h index 09f763d0742..6df956a6ab6 100644 --- a/absl/base/internal/atomic_hook.h +++ b/absl/base/internal/atomic_hook.h @@ -63,7 +63,7 @@ class AtomicHook { explicit constexpr AtomicHook(FnPtr default_fn) : hook_(default_fn), default_fn_(default_fn) {} #else - // On MSVC, this function sometimes executes after dynamic initiazliation =(. + // On MSVC, this function sometimes executes after dynamic initialization =(. // If a non-zero `hook_` has been installed by a dynamic initializer, we want // to preserve it. If not, `hook_` will be zero initialized and we have no // need to set it to `kUninitialized`. diff --git a/absl/base/internal/raw_logging.cc b/absl/base/internal/raw_logging.cc index 878fe6c6c2c..d20bf34ef53 100644 --- a/absl/base/internal/raw_logging.cc +++ b/absl/base/internal/raw_logging.cc @@ -37,9 +37,9 @@ // this, consider moving both to config.h instead. #if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \ defined(__Fuchsia__) || defined(__native_client__) || \ - defined(__EMSCRIPTEN__) -#include + defined(__EMSCRIPTEN__) || defined(__ASYLO__) +#include #define ABSL_HAVE_POSIX_WRITE 1 #define ABSL_LOW_LEVEL_WRITE_SUPPORTED 1 diff --git a/absl/base/internal/sysinfo.cc b/absl/base/internal/sysinfo.cc index 93039bf23f1..68d4266b70c 100644 --- a/absl/base/internal/sysinfo.cc +++ b/absl/base/internal/sysinfo.cc @@ -17,7 +17,6 @@ #include "absl/base/attributes.h" #ifdef _WIN32 -#include #include #else #include @@ -76,14 +75,23 @@ static int GetNumCPUs() { #if defined(_WIN32) static double GetNominalCPUFrequency() { - DWORD data; - DWORD data_size = sizeof(data); - #pragma comment(lib, "shlwapi.lib") // For SHGetValue(). - if (SUCCEEDED( - SHGetValueA(HKEY_LOCAL_MACHINE, - "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", - "~MHz", nullptr, &data, &data_size))) { - return data * 1e6; // Value is MHz. +#pragma comment(lib, "advapi32.lib") // For Reg* functions. + HKEY key; + // Use the Reg* functions rather than the SH functions because shlwapi.dll + // pulls in gdi32.dll which makes process destruction much more costly. + if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, + "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", 0, + KEY_READ, &key) == ERROR_SUCCESS) { + DWORD type = 0; + DWORD data = 0; + DWORD data_size = sizeof(data); + auto result = RegQueryValueExA(key, "~MHz", 0, &type, + reinterpret_cast(&data), &data_size); + RegCloseKey(key); + if (result == ERROR_SUCCESS && type == REG_DWORD && + data_size == sizeof(data)) { + return data * 1e6; // Value is MHz. + } } return 1.0; } diff --git a/absl/base/macros.h b/absl/base/macros.h index d481288f35f..d414dcace38 100644 --- a/absl/base/macros.h +++ b/absl/base/macros.h @@ -31,6 +31,7 @@ #include #include +#include "absl/base/attributes.h" #include "absl/base/optimization.h" #include "absl/base/port.h" @@ -176,12 +177,9 @@ enum LinkerInitialized { // ABSL_BAD_CALL_IF(c <= -1 || c > 255, // "'c' must have the value of an unsigned char or EOF"); // #endif // ABSL_BAD_CALL_IF - -#if defined(__clang__) -# if __has_attribute(enable_if) -# define ABSL_BAD_CALL_IF(expr, msg) \ - __attribute__((enable_if(expr, "Bad call trap"), unavailable(msg))) -# endif +#if ABSL_HAVE_ATTRIBUTE(enable_if) +#define ABSL_BAD_CALL_IF(expr, msg) \ + __attribute__((enable_if(expr, "Bad call trap"), unavailable(msg))) #endif // ABSL_ASSERT() diff --git a/absl/flags/flag.h b/absl/flags/flag.h index 09af47a06d1..52c3cede690 100644 --- a/absl/flags/flag.h +++ b/absl/flags/flag.h @@ -96,12 +96,12 @@ class Flag { constexpr Flag(const char* name, const flags_internal::HelpGenFunc help_gen, const char* filename, const flags_internal::FlagMarshallingOpFn marshalling_op, - const flags_internal::InitialValGenFunc initial_value_gen) + const flags_internal::FlagDfltGenFunc default_value_gen) : name_(name), help_gen_(help_gen), filename_(filename), marshalling_op_(marshalling_op), - initial_value_gen_(initial_value_gen), + default_value_gen_(default_value_gen), inited_(false), impl_(nullptr) {} #endif @@ -118,7 +118,7 @@ class Flag { name_, {flags_internal::FlagHelpSrc(help_gen_), flags_internal::FlagHelpSrcKind::kGenFunc}, - filename_, marshalling_op_, initial_value_gen_); + filename_, marshalling_op_, default_value_gen_); inited_.store(true, std::memory_order_release); } @@ -157,7 +157,7 @@ class Flag { const flags_internal::HelpGenFunc help_gen_; const char* filename_; const flags_internal::FlagMarshallingOpFn marshalling_op_; - const flags_internal::InitialValGenFunc initial_value_gen_; + const flags_internal::FlagDfltGenFunc default_value_gen_; mutable std::atomic inited_; mutable flags_internal::Flag* impl_; diff --git a/absl/flags/internal/flag.cc b/absl/flags/internal/flag.cc index d087f79e891..435cc362478 100644 --- a/absl/flags/internal/flag.cc +++ b/absl/flags/internal/flag.cc @@ -49,12 +49,11 @@ void FlagImpl::Init() { absl::MutexLock lock(&locks_->primary_mu); - if (def_ != nullptr) { + if (cur_ != nullptr) { inited_.store(true, std::memory_order_release); } else { - // Need to initialize def and cur fields. - def_ = (*initial_value_gen_)(); - cur_ = Clone(op_, def_); + // Need to initialize cur field. + cur_ = MakeInitValue().release(); StoreAtomic(); inited_.store(true, std::memory_order_release); InvokeCallback(); @@ -63,8 +62,7 @@ void FlagImpl::Init() { // Ensures that the lazily initialized data is initialized, // and returns pointer to the mutex guarding flags data. -absl::Mutex* FlagImpl::DataGuard() const - ABSL_LOCK_RETURNED(locks_->primary_mu) { +absl::Mutex* FlagImpl::DataGuard() const { if (ABSL_PREDICT_FALSE(!inited_.load(std::memory_order_acquire))) { const_cast(this)->Init(); } @@ -79,12 +77,23 @@ void FlagImpl::Destroy() const { // Values are heap allocated for Abseil Flags. if (cur_) Delete(op_, cur_); - if (def_) Delete(op_, def_); + if (def_kind_ == FlagDefaultSrcKind::kDynamicValue) + Delete(op_, default_src_.dynamic_value); } delete locks_; } +std::unique_ptr FlagImpl::MakeInitValue() const { + void* res = nullptr; + if (def_kind_ == FlagDefaultSrcKind::kDynamicValue) { + res = Clone(op_, default_src_.dynamic_value); + } else { + res = (*default_src_.gen_func)(); + } + return {res, DynValueDeleter{op_}}; +} + std::string FlagImpl::Help() const { return help_source_kind_ == FlagHelpSrcKind::kLiteral ? help_.literal : help_.gen_func(); @@ -103,7 +112,8 @@ bool FlagImpl::IsSpecifiedOnCommandLine() const { std::string FlagImpl::DefaultValue() const { absl::MutexLock l(DataGuard()); - return Unparse(marshalling_op_, def_); + auto obj = MakeInitValue(); + return Unparse(marshalling_op_, obj.get()); } std::string FlagImpl::CurrentValue() const { @@ -121,8 +131,7 @@ void FlagImpl::SetCallback( InvokeCallback(); } -void FlagImpl::InvokeCallback() const - ABSL_EXCLUSIVE_LOCKS_REQUIRED(locks_->primary_mu) { +void FlagImpl::InvokeCallback() const { if (!callback_) return; // If the flag has a mutation callback this function invokes it. While the @@ -172,23 +181,20 @@ bool FlagImpl::RestoreState(const CommandLineFlag& flag, const void* value, // '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 FlagImpl::TryParse(const CommandLineFlag& flag, void* dst, - absl::string_view value, std::string* err) const - ABSL_EXCLUSIVE_LOCKS_REQUIRED(locks_->primary_mu) { - void* tentative_value = Clone(op_, def_); + absl::string_view value, std::string* err) const { + auto tentative_value = MakeInitValue(); std::string parse_err; - if (!Parse(marshalling_op_, value, tentative_value, &parse_err)) { + if (!Parse(marshalling_op_, value, tentative_value.get(), &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(op_, tentative_value); return false; } - Copy(op_, tentative_value, dst); - Delete(op_, tentative_value); + Copy(op_, tentative_value.get(), dst); return true; } @@ -208,7 +214,7 @@ void FlagImpl::Read(const CommandLineFlag& flag, void* dst, CopyConstruct(op_, cur_, dst); } -void FlagImpl::StoreAtomic() ABSL_EXCLUSIVE_LOCKS_REQUIRED(locks_->primary_mu) { +void FlagImpl::StoreAtomic() { size_t data_size = Sizeof(op_); if (data_size <= sizeof(int64_t)) { @@ -299,12 +305,22 @@ bool FlagImpl::SetFromString(const CommandLineFlag& flag, break; } case SET_FLAGS_DEFAULT: { - // modify the flag's default-value - if (!TryParse(flag, def_, value, err)) return false; + // Flag's new default-value. + auto new_default_value = MakeInitValue(); + + if (!TryParse(flag, new_default_value.get(), value, err)) return false; + + if (def_kind_ == FlagDefaultSrcKind::kDynamicValue) { + // Release old default value. + Delete(op_, default_src_.dynamic_value); + } + + default_src_.dynamic_value = new_default_value.release(); + def_kind_ = FlagDefaultSrcKind::kDynamicValue; if (!modified_) { // Need to set both default value *and* current, in this case - Copy(op_, def_, cur_); + Copy(op_, default_src_.dynamic_value, cur_); StoreAtomic(); InvokeCallback(); } @@ -321,9 +337,9 @@ void FlagImpl::CheckDefaultValueParsingRoundtrip( absl::MutexLock lock(DataGuard()); - void* dst = Clone(op_, def_); + auto dst = MakeInitValue(); std::string error; - if (!flags_internal::Parse(marshalling_op_, v, dst, &error)) { + if (!flags_internal::Parse(marshalling_op_, v, dst.get(), &error)) { ABSL_INTERNAL_LOG( FATAL, absl::StrCat("Flag ", flag.Name(), " (from ", flag.Filename(), @@ -333,18 +349,15 @@ void FlagImpl::CheckDefaultValueParsingRoundtrip( // We do not compare dst to def since parsing/unparsing may make // small changes, e.g., precision loss for floating point types. - Delete(op_, dst); } bool FlagImpl::ValidateInputValue(absl::string_view value) const { absl::MutexLock l(DataGuard()); - void* obj = Clone(op_, def_); + auto obj = MakeInitValue(); std::string ignored_error; - const bool result = - flags_internal::Parse(marshalling_op_, value, obj, &ignored_error); - Delete(op_, obj); - return result; + return flags_internal::Parse(marshalling_op_, value, obj.get(), + &ignored_error); } } // namespace flags_internal diff --git a/absl/flags/internal/flag.h b/absl/flags/internal/flag.h index d79902543a2..947a8cadaf2 100644 --- a/absl/flags/internal/flag.h +++ b/absl/flags/internal/flag.h @@ -119,47 +119,61 @@ constexpr flags_internal::HelpInitArg HelpArg(char) { flags_internal::FlagHelpSrcKind::kGenFunc}; } -// Signature for the function generating the initial flag value based (usually +// Signature for the function generating the initial flag value (usually // based on default value supplied in flag's definition) -using InitialValGenFunc = void* (*)(); +using FlagDfltGenFunc = void* (*)(); + +union FlagDefaultSrc { + constexpr explicit FlagDefaultSrc(FlagDfltGenFunc gen_func_arg) + : gen_func(gen_func_arg) {} + + void* dynamic_value; + FlagDfltGenFunc gen_func; +}; + +enum class FlagDefaultSrcKind : int8_t { kDynamicValue, kGenFunc }; // 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 (*)(); -void InvokeCallback(absl::Mutex* primary_mu, absl::Mutex* callback_mu, - FlagCallback cb) ABSL_EXCLUSIVE_LOCKS_REQUIRED(primary_mu); +struct DynValueDeleter { + void operator()(void* ptr) const { Delete(op, ptr); } + + const FlagOpFn op; +}; // The class encapsulates the Flag's data and safe access to it. class FlagImpl { public: constexpr FlagImpl(const flags_internal::FlagOpFn op, const flags_internal::FlagMarshallingOpFn marshalling_op, - const flags_internal::InitialValGenFunc initial_value_gen, + const flags_internal::FlagDfltGenFunc default_value_gen, const HelpInitArg help) : op_(op), marshalling_op_(marshalling_op), - initial_value_gen_(initial_value_gen), help_(help.source), - help_source_kind_(help.kind) {} + help_source_kind_(help.kind), + def_kind_(flags_internal::FlagDefaultSrcKind::kGenFunc), + default_src_(default_value_gen) {} // Forces destruction of the Flag's data. void Destroy() const; // Constant access methods std::string Help() const; - bool IsModified() const ABSL_LOCKS_EXCLUDED(locks_->primary_mu); - bool IsSpecifiedOnCommandLine() const ABSL_LOCKS_EXCLUDED(locks_->primary_mu); - std::string DefaultValue() const ABSL_LOCKS_EXCLUDED(locks_->primary_mu); - std::string CurrentValue() const ABSL_LOCKS_EXCLUDED(locks_->primary_mu); + bool IsModified() const ABSL_LOCKS_EXCLUDED(*DataGuard()); + bool IsSpecifiedOnCommandLine() const ABSL_LOCKS_EXCLUDED(*DataGuard()); + std::string DefaultValue() const ABSL_LOCKS_EXCLUDED(*DataGuard()); + std::string CurrentValue() const ABSL_LOCKS_EXCLUDED(*DataGuard()); void Read(const CommandLineFlag& flag, void* dst, const flags_internal::FlagOpFn dst_op) const - ABSL_LOCKS_EXCLUDED(locks_->primary_mu); + ABSL_LOCKS_EXCLUDED(*DataGuard()); // Attempts to parse supplied `value` std::string. bool TryParse(const CommandLineFlag& flag, void* dst, absl::string_view value, std::string* err) const - ABSL_EXCLUSIVE_LOCKS_REQUIRED(locks_->primary_mu); + ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard()); template bool AtomicGet(T* v) const { const int64_t r = atomic_.load(std::memory_order_acquire); @@ -174,23 +188,23 @@ class FlagImpl { // Mutating access methods void Write(const CommandLineFlag& flag, const void* src, const flags_internal::FlagOpFn src_op) - ABSL_LOCKS_EXCLUDED(locks_->primary_mu); + ABSL_LOCKS_EXCLUDED(*DataGuard()); bool SetFromString(const CommandLineFlag& flag, absl::string_view value, FlagSettingMode set_mode, ValueSource source, - std::string* err) ABSL_LOCKS_EXCLUDED(locks_->primary_mu); + std::string* err) ABSL_LOCKS_EXCLUDED(*DataGuard()); // If possible, updates copy of the Flag's value that is stored in an // atomic word. - void StoreAtomic() ABSL_EXCLUSIVE_LOCKS_REQUIRED(locks_->primary_mu); + void StoreAtomic() ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard()); // Interfaces to operate on callbacks. void SetCallback(const flags_internal::FlagCallback mutation_callback) - ABSL_LOCKS_EXCLUDED(locks_->primary_mu); - void InvokeCallback() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(locks_->primary_mu); + ABSL_LOCKS_EXCLUDED(*DataGuard()); + void InvokeCallback() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard()); // Interfaces to save/restore mutable flag data template std::unique_ptr SaveState( - Flag* flag) const ABSL_LOCKS_EXCLUDED(locks_->primary_mu) { + Flag* flag) const ABSL_LOCKS_EXCLUDED(*DataGuard()) { T&& cur_value = flag->Get(); absl::MutexLock l(DataGuard()); @@ -199,13 +213,13 @@ class FlagImpl { } bool RestoreState(const CommandLineFlag& flag, const void* value, bool modified, bool on_command_line, int64_t counter) - ABSL_LOCKS_EXCLUDED(locks_->primary_mu); + ABSL_LOCKS_EXCLUDED(*DataGuard()); // Value validation interfaces. void CheckDefaultValueParsingRoundtrip(const CommandLineFlag& flag) const - ABSL_LOCKS_EXCLUDED(locks_->primary_mu); + ABSL_LOCKS_EXCLUDED(*DataGuard()); bool ValidateInputValue(absl::string_view value) const - ABSL_LOCKS_EXCLUDED(locks_->primary_mu); + ABSL_LOCKS_EXCLUDED(*DataGuard()); private: // Lazy initialization of the Flag's data. @@ -214,27 +228,36 @@ class FlagImpl { // and returns pointer to the mutex guarding flags data. absl::Mutex* DataGuard() const ABSL_LOCK_RETURNED(locks_->primary_mu); + // Returns heap allocated value of type T initialized with default value. + std::unique_ptr MakeInitValue() const + ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard()); + // Immutable Flag's data. - const FlagOpFn op_; // Type-specific handler. - const FlagMarshallingOpFn marshalling_op_; // Marshalling ops handler. - const InitialValGenFunc initial_value_gen_; // Makes flag's initial value. + const FlagOpFn op_; // Type-specific handler. + const FlagMarshallingOpFn marshalling_op_; // Marshalling ops handler. const FlagHelpSrc help_; // Help message literal or function to generate it. // Indicates if help message was supplied as literal or generator func. const FlagHelpSrcKind help_source_kind_; - // Mutable Flag's data. (guarded by locks_->primary_mu). - // Indicates that locks_, cur_ and def_ fields have been lazily initialized. + // Mutable Flag's data. (guarded by DataGuard()). + // Indicates that locks_ and cur_ fields have been lazily initialized. std::atomic inited_{false}; // Has flag value been modified? - bool modified_ ABSL_GUARDED_BY(locks_->primary_mu) = false; + bool modified_ ABSL_GUARDED_BY(*DataGuard()) = false; // Specified on command line. - bool on_command_line_ ABSL_GUARDED_BY(locks_->primary_mu) = false; - // Lazily initialized pointer to default value - void* def_ ABSL_GUARDED_BY(locks_->primary_mu) = nullptr; + bool on_command_line_ ABSL_GUARDED_BY(*DataGuard()) = false; + // If def_kind_ == kDynamicValue, default_src_ holds a dynamically allocated + // value. + FlagDefaultSrcKind def_kind_ ABSL_GUARDED_BY(*DataGuard()); + // Either a pointer to the function generating the default value based on the + // value specified in ABSL_FLAG or pointer to the dynamically set default + // value via SetCommandLineOptionWithMode. def_kind_ is used to distinguish + // these two cases. + FlagDefaultSrc default_src_ ABSL_GUARDED_BY(*DataGuard()); // Lazily initialized pointer to current value - void* cur_ ABSL_GUARDED_BY(locks_->primary_mu) = nullptr; + void* cur_ ABSL_GUARDED_BY(*DataGuard()) = nullptr; // Mutation counter - int64_t counter_ ABSL_GUARDED_BY(locks_->primary_mu) = 0; + int64_t counter_ ABSL_GUARDED_BY(*DataGuard()) = 0; // For some types, a copy of the current value is kept in an atomically // accessible field. std::atomic atomic_{flags_internal::AtomicInit()}; @@ -263,7 +286,7 @@ class Flag final : public flags_internal::CommandLineFlag { constexpr Flag(const char* name, const flags_internal::HelpInitArg help, const char* filename, const flags_internal::FlagMarshallingOpFn marshalling_op, - const flags_internal::InitialValGenFunc initial_value_gen) + const flags_internal::FlagDfltGenFunc initial_value_gen) : flags_internal::CommandLineFlag(name, filename), impl_(&flags_internal::FlagOps, marshalling_op, initial_value_gen, help) {} diff --git a/absl/random/internal/BUILD.bazel b/absl/random/internal/BUILD.bazel index 5026e2b2722..952929ea264 100644 --- a/absl/random/internal/BUILD.bazel +++ b/absl/random/internal/BUILD.bazel @@ -297,6 +297,7 @@ cc_library( linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":platform", + "//absl/base:core_headers", ], ) @@ -659,6 +660,7 @@ cc_library( deps = [ ":platform", ":randen_engine", + "//absl/base:core_headers", "//absl/base:raw_logging_internal", ], ) diff --git a/absl/random/internal/nanobenchmark.cc b/absl/random/internal/nanobenchmark.cc index 7f37800c683..feb81c85958 100644 --- a/absl/random/internal/nanobenchmark.cc +++ b/absl/random/internal/nanobenchmark.cc @@ -27,6 +27,7 @@ #include #include +#include "absl/base/attributes.h" #include "absl/base/internal/raw_logging.h" #include "absl/random/internal/platform.h" #include "absl/random/internal/randen_engine.h" @@ -59,15 +60,6 @@ #include // 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)) diff --git a/absl/random/internal/randen_slow.cc b/absl/random/internal/randen_slow.cc index 7a2e2daa53f..e7959c7e052 100644 --- a/absl/random/internal/randen_slow.cc +++ b/absl/random/internal/randen_slow.cc @@ -18,17 +18,9 @@ #include #include +#include "absl/base/attributes.h" #include "absl/random/internal/platform.h" -// ABSL_HAVE_ATTRIBUTE -#if !defined(ABSL_HAVE_ATTRIBUTE) -#ifdef __has_attribute -#define ABSL_HAVE_ATTRIBUTE(x) __has_attribute(x) -#else -#define ABSL_HAVE_ATTRIBUTE(x) 0 -#endif -#endif - #if ABSL_HAVE_ATTRIBUTE(always_inline) || \ (defined(__GNUC__) && !defined(__clang__)) #define ABSL_RANDOM_INTERNAL_ATTRIBUTE_ALWAYS_INLINE \ diff --git a/absl/random/internal/seed_material.cc b/absl/random/internal/seed_material.cc index 85dd535f9c0..ab4dd0c26b0 100644 --- a/absl/random/internal/seed_material.cc +++ b/absl/random/internal/seed_material.cc @@ -45,6 +45,9 @@ #define ABSL_RANDOM_USE_BCRYPT 1 #pragma comment(lib, "bcrypt.lib") +#elif defined(__Fuchsia__) +#include + #endif #if defined(ABSL_RANDOM_USE_BCRYPT) @@ -107,6 +110,15 @@ bool ReadSeedMaterialFromOSEntropyImpl(absl::Span values) { return true; } +#elif defined(__Fuchsia__) + +bool ReadSeedMaterialFromOSEntropyImpl(absl::Span values) { + auto buffer = reinterpret_cast(values.data()); + size_t buffer_size = sizeof(uint32_t) * values.size(); + zx_cprng_draw(buffer, buffer_size); + return true; +} + #else // On *nix, read entropy from /dev/urandom. diff --git a/absl/strings/internal/str_format/bind.h b/absl/strings/internal/str_format/bind.h index 7f46035113b..dafcd6102f9 100644 --- a/absl/strings/internal/str_format/bind.h +++ b/absl/strings/internal/str_format/bind.h @@ -73,7 +73,7 @@ class FormatSpecTemplate using Base = typename MakeDependent::type; public: -#if ABSL_INTERNAL_ENABLE_FORMAT_CHECKER +#ifdef ABSL_INTERNAL_ENABLE_FORMAT_CHECKER // Honeypot overload for when the std::string is not constexpr. // We use the 'unavailable' attribute to give a better compiler error than diff --git a/absl/strings/internal/str_format/checker.h b/absl/strings/internal/str_format/checker.h index 8b594f2d5cc..04a88827518 100644 --- a/absl/strings/internal/str_format/checker.h +++ b/absl/strings/internal/str_format/checker.h @@ -1,17 +1,16 @@ #ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_CHECKER_H_ #define ABSL_STRINGS_INTERNAL_STR_FORMAT_CHECKER_H_ +#include "absl/base/attributes.h" #include "absl/strings/internal/str_format/arg.h" #include "absl/strings/internal/str_format/extension.h" // Compile time check support for entry points. #ifndef ABSL_INTERNAL_ENABLE_FORMAT_CHECKER -#if defined(__clang__) && !defined(__native_client__) -#if __has_attribute(enable_if) +#if ABSL_HAVE_ATTRIBUTE(enable_if) && !defined(__native_client__) #define ABSL_INTERNAL_ENABLE_FORMAT_CHECKER 1 -#endif // __has_attribute(enable_if) -#endif // defined(__clang__) && !defined(__native_client__) +#endif // ABSL_HAVE_ATTRIBUTE(enable_if) && !defined(__native_client__) #endif // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER namespace absl { @@ -31,7 +30,7 @@ constexpr Conv ArgumentToConv() { std::declval()))::kConv; } -#if ABSL_INTERNAL_ENABLE_FORMAT_CHECKER +#ifdef ABSL_INTERNAL_ENABLE_FORMAT_CHECKER constexpr bool ContainsChar(const char* chars, char c) { return *chars == c || (*chars && ContainsChar(chars + 1, c)); diff --git a/absl/strings/internal/str_format/checker_test.cc b/absl/strings/internal/str_format/checker_test.cc index c1d8c769ef7..7aa194a7fa9 100644 --- a/absl/strings/internal/str_format/checker_test.cc +++ b/absl/strings/internal/str_format/checker_test.cc @@ -35,7 +35,7 @@ TEST(StrFormatChecker, ArgumentToConv) { EXPECT_EQ(ConvToString(conv), "p"); } -#if ABSL_INTERNAL_ENABLE_FORMAT_CHECKER +#ifdef ABSL_INTERNAL_ENABLE_FORMAT_CHECKER struct Case { bool result; diff --git a/absl/strings/internal/str_format/parser.h b/absl/strings/internal/str_format/parser.h index b7614a09fa4..4b441f7193a 100644 --- a/absl/strings/internal/str_format/parser.h +++ b/absl/strings/internal/str_format/parser.h @@ -274,7 +274,7 @@ template class ExtendedParsedFormat : public str_format_internal::ParsedFormatBase { public: explicit ExtendedParsedFormat(string_view format) -#if ABSL_INTERNAL_ENABLE_FORMAT_CHECKER +#ifdef ABSL_INTERNAL_ENABLE_FORMAT_CHECKER __attribute__(( enable_if(str_format_internal::EnsureConstexpr(format), "Format std::string is not constexpr."), diff --git a/absl/time/BUILD.bazel b/absl/time/BUILD.bazel index a615152f01b..9ab2adb8866 100644 --- a/absl/time/BUILD.bazel +++ b/absl/time/BUILD.bazel @@ -93,6 +93,7 @@ cc_test( ":time", "//absl/base:config", "//absl/base:core_headers", + "//absl/numeric:int128", "//absl/time/internal/cctz:time_zone", "@com_google_googletest//:gtest_main", ], diff --git a/absl/time/time_test.cc b/absl/time/time_test.cc index 9c4709e6b46..6f89672c66d 100644 --- a/absl/time/time_test.cc +++ b/absl/time/time_test.cc @@ -27,6 +27,7 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "absl/numeric/int128.h" #include "absl/time/clock.h" #include "absl/time/internal/test_util.h" @@ -575,6 +576,50 @@ TEST(Time, ToChronoTime) { absl::ToChronoTime(absl::UnixEpoch() - tick)); } +// Check that absl::int128 works as a std::chrono::duration representation. +TEST(Time, Chrono128) { + // Define a std::chrono::time_point type whose time[sic]_since_epoch() is + // a signed 128-bit count of attoseconds. This has a range and resolution + // (currently) beyond those of absl::Time, and undoubtedly also beyond those + // of std::chrono::system_clock::time_point. + // + // Note: The to/from-chrono support should probably be updated to handle + // such wide representations. + using Timestamp = + std::chrono::time_point>; + + // Expect that we can round-trip the std::chrono::system_clock::time_point + // extremes through both absl::Time and Timestamp, and that Timestamp can + // handle the (current) absl::Time extremes. + // + // Note: We should use std::chrono::floor() instead of time_point_cast(), + // but floor() is only available since c++17. + for (const auto tp : {std::chrono::system_clock::time_point::min(), + std::chrono::system_clock::time_point::max()}) { + EXPECT_EQ(tp, absl::ToChronoTime(absl::FromChrono(tp))); + EXPECT_EQ(tp, std::chrono::time_point_cast< + std::chrono::system_clock::time_point::duration>( + std::chrono::time_point_cast(tp))); + } + Timestamp::duration::rep v = std::numeric_limits::min(); + v *= Timestamp::duration::period::den; + auto ts = Timestamp(Timestamp::duration(v)); + ts += std::chrono::duration(0); + EXPECT_EQ(std::numeric_limits::min(), + ts.time_since_epoch().count() / Timestamp::duration::period::den); + EXPECT_EQ(0, + ts.time_since_epoch().count() % Timestamp::duration::period::den); + v = std::numeric_limits::max(); + v *= Timestamp::duration::period::den; + ts = Timestamp(Timestamp::duration(v)); + ts += std::chrono::duration(999999999750000000); + EXPECT_EQ(std::numeric_limits::max(), + ts.time_since_epoch().count() / Timestamp::duration::period::den); + EXPECT_EQ(999999999750000000, + ts.time_since_epoch().count() % Timestamp::duration::period::den); +} + TEST(Time, TimeZoneAt) { const absl::TimeZone nyc = absl::time_internal::LoadTimeZone("America/New_York"); From d659fe54b35ab9b8e35c72e50a4b8814167d5a84 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Wed, 4 Dec 2019 14:13:43 -0800 Subject: [PATCH 0026/3164] Export of internal Abseil changes -- c385118b3ef0528d150bfe7aeeb63e77f9e463cd by Matt Calabrese : Internal-only Archetype generation for testing generic code with user-defined types of various properties. PiperOrigin-RevId: 283833099 -- 4ccf340d3b295aa5b796ee5c97128b61d38899ea by Derek Mauro : Fixes the flags parse_test. Windows doesn't like consecutive path separators. PiperOrigin-RevId: 283614649 -- 5df6d83acb1e49cd1da785cfaf7551f05149f3c9 by Andy Getzendanner : ABSL_INTERNAL_LOG: forward complete __FILE__ to internal_log_function; not just basename. PiperOrigin-RevId: 283406080 GitOrigin-RevId: c385118b3ef0528d150bfe7aeeb63e77f9e463cd Change-Id: Ib0782354691a73fc40185c3262cfd507085b3393 --- absl/base/internal/raw_logging.h | 12 +- absl/flags/parse_test.cc | 16 +- absl/meta/type_traits.h | 45 +- absl/meta/type_traits_test.cc | 21 - absl/types/BUILD.bazel | 34 + absl/types/CMakeLists.txt | 46 + absl/types/internal/conformance_aliases.h | 445 +++++++ absl/types/internal/conformance_archetype.h | 976 ++++++++++++++ absl/types/internal/conformance_profile.h | 374 ++++++ .../internal/conformance_testing_test.cc | 1186 +++++++++++++++++ 10 files changed, 3112 insertions(+), 43 deletions(-) create mode 100644 absl/types/internal/conformance_aliases.h create mode 100644 absl/types/internal/conformance_archetype.h create mode 100644 absl/types/internal/conformance_profile.h create mode 100644 absl/types/internal/conformance_testing_test.cc diff --git a/absl/base/internal/raw_logging.h b/absl/base/internal/raw_logging.h index 6a4c093603f..49c8dfbf1b0 100644 --- a/absl/base/internal/raw_logging.h +++ b/absl/base/internal/raw_logging.h @@ -70,14 +70,10 @@ // // The API is a subset of the above: each macro only takes two arguments. Use // StrCat if you need to build a richer message. -#define ABSL_INTERNAL_LOG(severity, message) \ - do { \ - constexpr const char* absl_raw_logging_internal_basename = \ - ::absl::raw_logging_internal::Basename(__FILE__, \ - sizeof(__FILE__) - 1); \ - ::absl::raw_logging_internal::internal_log_function( \ - ABSL_RAW_LOGGING_INTERNAL_##severity, \ - absl_raw_logging_internal_basename, __LINE__, message); \ +#define ABSL_INTERNAL_LOG(severity, message) \ + do { \ + ::absl::raw_logging_internal::internal_log_function( \ + ABSL_RAW_LOGGING_INTERNAL_##severity, __FILE__, __LINE__, message); \ } while (0) #define ABSL_INTERNAL_CHECK(condition, message) \ diff --git a/absl/flags/parse_test.cc b/absl/flags/parse_test.cc index 447a3bc7bdc..f89fa96527b 100644 --- a/absl/flags/parse_test.cc +++ b/absl/flags/parse_test.cc @@ -22,6 +22,7 @@ #include "absl/base/internal/raw_logging.h" #include "absl/base/internal/scoped_set_env.h" #include "absl/flags/flag.h" +#include "absl/strings/match.h" #include "absl/strings/str_cat.h" #include "absl/strings/substitute.h" #include "absl/types/span.h" @@ -92,8 +93,11 @@ const std::string& GetTestTempDir() { auto len = GetTempPathA(MAX_PATH, temp_path_buffer); if (len < MAX_PATH && len != 0) { - std::string temp_dir_name = absl::StrCat( - temp_path_buffer, "\\parse_test.", GetCurrentProcessId()); + std::string temp_dir_name = temp_path_buffer; + if (!absl::EndsWith(temp_dir_name, "\\")) { + temp_dir_name.push_back('\\'); + } + absl::StrAppend(&temp_dir_name, "parse_test.", GetCurrentProcessId()); if (CreateDirectoryA(temp_dir_name.c_str(), nullptr)) { *res = temp_dir_name; } @@ -104,11 +108,11 @@ const std::string& GetTestTempDir() { *res = unique_name; } #endif + } - if (res->empty()) { - ABSL_INTERNAL_LOG(FATAL, - "Failed to make temporary directory for data files"); - } + if (res->empty()) { + ABSL_INTERNAL_LOG(FATAL, + "Failed to make temporary directory for data files"); } #ifdef _WIN32 diff --git a/absl/meta/type_traits.h b/absl/meta/type_traits.h index d57a8792313..8cd5f043e0b 100644 --- a/absl/meta/type_traits.h +++ b/absl/meta/type_traits.h @@ -41,8 +41,18 @@ #include "absl/base/config.h" +// MSVC constructibility traits do not detect destructor properties and so our +// implementations should not use them as a source-of-truth. +#if defined(_MSC_VER) && !defined(__clang__) && !defined(__GNUC__) +#define ABSL_META_INTERNAL_STD_CONSTRUCTION_TRAITS_DONT_CHECK_DESTRUCTION 1 +#endif + namespace absl { +// Defined and documented later on in this file. +template +struct is_trivially_destructible; + // Defined and documented later on in this file. template struct is_trivially_move_assignable; @@ -65,6 +75,20 @@ union SingleMemberUnion { #pragma warning(pop) #endif // defined(_MSC_VER) && !defined(__GNUC__) +template +struct IsTriviallyMoveConstructibleObject + : std::integral_constant< + bool, std::is_move_constructible< + type_traits_internal::SingleMemberUnion>::value && + absl::is_trivially_destructible::value> {}; + +template +struct IsTriviallyCopyConstructibleObject + : std::integral_constant< + bool, std::is_copy_constructible< + type_traits_internal::SingleMemberUnion>::value && + absl::is_trivially_destructible::value> {}; + template struct IsTriviallyMoveAssignableReference : std::false_type {}; @@ -323,7 +347,9 @@ struct is_trivially_default_constructible : std::integral_constant::value && is_trivially_destructible::value> { -#ifdef ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE +#if defined(ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE) && \ + !defined( \ + ABSL_META_INTERNAL_STD_CONSTRUCTION_TRAITS_DONT_CHECK_DESTRUCTION) private: static constexpr bool compliant = std::is_trivially_default_constructible::value == @@ -354,10 +380,11 @@ template struct is_trivially_move_constructible : std::conditional< std::is_object::value && !std::is_array::value, - std::is_move_constructible< - type_traits_internal::SingleMemberUnion>, + type_traits_internal::IsTriviallyMoveConstructibleObject, std::is_reference>::type::type { -#ifdef ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE +#if defined(ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE) && \ + !defined( \ + ABSL_META_INTERNAL_STD_CONSTRUCTION_TRAITS_DONT_CHECK_DESTRUCTION) private: static constexpr bool compliant = std::is_trivially_move_constructible::value == @@ -388,10 +415,11 @@ template struct is_trivially_copy_constructible : std::conditional< std::is_object::value && !std::is_array::value, - std::is_copy_constructible< - type_traits_internal::SingleMemberUnion>, + type_traits_internal::IsTriviallyCopyConstructibleObject, std::is_lvalue_reference>::type::type { -#ifdef ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE +#if defined(ABSL_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE) && \ + !defined( \ + ABSL_META_INTERNAL_STD_CONSTRUCTION_TRAITS_DONT_CHECK_DESTRUCTION) private: static constexpr bool compliant = std::is_trivially_copy_constructible::value == @@ -423,7 +451,8 @@ struct is_trivially_copy_constructible template struct is_trivially_move_assignable : std::conditional< - std::is_object::value && !std::is_array::value, + std::is_object::value && !std::is_array::value && + std::is_move_assignable::value, std::is_move_assignable>, type_traits_internal::IsTriviallyMoveAssignableReference>::type:: type { diff --git a/absl/meta/type_traits_test.cc b/absl/meta/type_traits_test.cc index 6fbb42f82e0..1aafd0d49a8 100644 --- a/absl/meta/type_traits_test.cc +++ b/absl/meta/type_traits_test.cc @@ -347,21 +347,6 @@ class Base { virtual ~Base() {} }; -// In GCC/Clang, std::is_trivially_constructible requires that the destructor is -// trivial. However, MSVC doesn't require that. This results in different -// behavior when checking is_trivially_constructible on any type with -// nontrivial destructor. Since absl::is_trivially_default_constructible and -// absl::is_trivially_copy_constructible both follows Clang/GCC's interpretation -// and check is_trivially_destructible, it results in inconsistency with -// std::is_trivially_xxx_constructible on MSVC. This macro is used to work -// around this issue in test. In practice, a trivially constructible type -// should also be trivially destructible. -// GCC bug 51452: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=51452 -// LWG issue 2116: http://cplusplus.github.io/LWG/lwg-active.html#2116 -#ifndef _MSC_VER -#define ABSL_TRIVIALLY_CONSTRUCTIBLE_VERIFY_TRIVIALLY_DESTRUCTIBLE 1 -#endif - // Old versions of libc++, around Clang 3.5 to 3.6, consider deleted destructors // as also being trivial. With the resolution of CWG 1928 and CWG 1734, this // is no longer considered true and has thus been amended. @@ -499,11 +484,9 @@ TEST(TypeTraitsTest, TestTrivialDefaultCtor) { EXPECT_FALSE( absl::is_trivially_default_constructible::value); -#ifdef ABSL_TRIVIALLY_CONSTRUCTIBLE_VERIFY_TRIVIALLY_DESTRUCTIBLE // types with nontrivial destructor are nontrivial EXPECT_FALSE( absl::is_trivially_default_constructible::value); -#endif // types with vtables EXPECT_FALSE(absl::is_trivially_default_constructible::value); @@ -607,11 +590,9 @@ TEST(TypeTraitsTest, TestTrivialMoveCtor) { EXPECT_FALSE( absl::is_trivially_move_constructible::value); -#ifdef ABSL_TRIVIALLY_CONSTRUCTIBLE_VERIFY_TRIVIALLY_DESTRUCTIBLE // type with nontrivial destructor are nontrivial move construbtible EXPECT_FALSE( absl::is_trivially_move_constructible::value); -#endif // types with vtables EXPECT_FALSE(absl::is_trivially_move_constructible::value); @@ -682,11 +663,9 @@ TEST(TypeTraitsTest, TestTrivialCopyCtor) { EXPECT_FALSE( absl::is_trivially_copy_constructible::value); -#ifdef ABSL_TRIVIALLY_CONSTRUCTIBLE_VERIFY_TRIVIALLY_DESTRUCTIBLE // type with nontrivial destructor are nontrivial copy construbtible EXPECT_FALSE( absl::is_trivially_copy_constructible::value); -#endif // types with vtables EXPECT_FALSE(absl::is_trivially_copy_constructible::value); diff --git a/absl/types/BUILD.bazel b/absl/types/BUILD.bazel index 236c24bc34d..f2ea9f395af 100644 --- a/absl/types/BUILD.bazel +++ b/absl/types/BUILD.bazel @@ -208,6 +208,40 @@ cc_test( ], ) +cc_library( + name = "conformance_testing", + testonly = 1, + hdrs = [ + "internal/conformance_aliases.h", + "internal/conformance_archetype.h", + "internal/conformance_profile.h", + ], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + "//absl/debugging:demangle_internal", + "//absl/meta:type_traits", + "//absl/strings", + "//absl/utility", + "@com_google_googletest//:gtest", + ], +) + +cc_test( + name = "conformance_testing_test", + size = "small", + srcs = [ + "internal/conformance_testing_test.cc", + ], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":conformance_testing", + "//absl/meta:type_traits", + "@com_google_googletest//:gtest_main", + ], +) + cc_library( name = "variant", srcs = ["internal/variant.h"], diff --git a/absl/types/CMakeLists.txt b/absl/types/CMakeLists.txt index 952efc34f63..c7c882507f6 100644 --- a/absl/types/CMakeLists.txt +++ b/absl/types/CMakeLists.txt @@ -238,6 +238,52 @@ absl_cc_test( gmock_main ) +absl_cc_library( + NAME + conformance_testing + HDRS + "internal/conformance_aliases.h" + "internal/conformance_archetype.h" + "internal/conformance_profile.h" + COPTS + ${ABSL_DEFAULT_COPTS} + DEPS + absl::debugging + absl::type_traits + absl::strings + absl::utility + gmock_main + PUBLIC +) + +absl_cc_test( + NAME + conformance_testing_test + SRCS + "internal/conformance_testing_test.cc" + COPTS + ${ABSL_TEST_COPTS} + ${ABSL_EXCEPTIONS_FLAG} + LINKOPTS + ${ABSL_EXCEPTIONS_FLAG_LINKOPTS} + DEPS + absl::conformance_testing + absl::type_traits + gmock_main +) + +absl_cc_test( + NAME + conformance_testing_test_no_exceptions + SRCS + "internal/conformance_testing_test.cc" + COPTS + ${ABSL_TEST_COPTS} + DEPS + absl::conformance_testing + gmock_main +) + absl_cc_library( NAME variant diff --git a/absl/types/internal/conformance_aliases.h b/absl/types/internal/conformance_aliases.h new file mode 100644 index 00000000000..7d5d0e092a4 --- /dev/null +++ b/absl/types/internal/conformance_aliases.h @@ -0,0 +1,445 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// regularity_aliases.h +// ----------------------------------------------------------------------------- +// +// This file contains type aliases of common ConformanceProfiles and Archetypes +// so that they can be directly used by name without creating them from scratch. + +#ifndef ABSL_TYPES_INTERNAL_CONFORMANCE_ALIASES_H_ +#define ABSL_TYPES_INTERNAL_CONFORMANCE_ALIASES_H_ + +#include "absl/types/internal/conformance_archetype.h" +#include "absl/types/internal/conformance_profile.h" + +namespace absl { +namespace types_internal { + +// Creates both a Profile and a corresponding Archetype with root name "name". +#define ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS(name, ...) \ + struct name##Profile : __VA_ARGS__ {}; \ + \ + using name##Archetype = ::absl::types_internal::Archetype; \ + \ + template \ + using name##Archetype##_ = ::absl::types_internal::Archetype< \ + ::absl::types_internal::StrongProfileTypedef> + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasTrivialDefaultConstructor, + ConformanceProfile); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasNothrowDefaultConstructor, + ConformanceProfile); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasDefaultConstructor, ConformanceProfile); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasTrivialMoveConstructor, ConformanceProfile); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasNothrowMoveConstructor, ConformanceProfile); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasMoveConstructor, + ConformanceProfile); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasTrivialCopyConstructor, + ConformanceProfile); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasNothrowCopyConstructor, + ConformanceProfile); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasCopyConstructor, + ConformanceProfile); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasTrivialMoveAssign, + ConformanceProfile); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasNothrowMoveAssign, + ConformanceProfile); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasMoveAssign, + ConformanceProfile); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasTrivialCopyAssign, + ConformanceProfile); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasNothrowCopyAssign, + ConformanceProfile); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasCopyAssign, + ConformanceProfile); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasTrivialDestructor, + ConformanceProfile); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasNothrowDestructor, + ConformanceProfile); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasDestructor, + ConformanceProfile); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasNothrowEquality, + ConformanceProfile); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasEquality, + ConformanceProfile); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasNothrowInequality, + ConformanceProfile); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasInequality, + ConformanceProfile); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasNothrowLessThan, + ConformanceProfile); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasLessThan, + ConformanceProfile); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasNothrowLessEqual, + ConformanceProfile); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasLessEqual, + ConformanceProfile); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasNothrowGreaterEqual, + ConformanceProfile< + default_constructible::maybe, move_constructible::maybe, + copy_constructible::maybe, move_assignable::maybe, + copy_assignable::maybe, destructible::maybe, equality_comparable::maybe, + inequality_comparable::maybe, less_than_comparable::maybe, + less_equal_comparable::maybe, greater_equal_comparable::nothrow>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasGreaterEqual, + ConformanceProfile< + default_constructible::maybe, move_constructible::maybe, + copy_constructible::maybe, move_assignable::maybe, + copy_assignable::maybe, destructible::maybe, equality_comparable::maybe, + inequality_comparable::maybe, less_than_comparable::maybe, + less_equal_comparable::maybe, greater_equal_comparable::yes>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasNothrowGreaterThan, + ConformanceProfile< + default_constructible::maybe, move_constructible::maybe, + copy_constructible::maybe, move_assignable::maybe, + copy_assignable::maybe, destructible::maybe, equality_comparable::maybe, + inequality_comparable::maybe, less_than_comparable::maybe, + less_equal_comparable::maybe, greater_equal_comparable::maybe, + greater_than_comparable::nothrow>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasGreaterThan, + ConformanceProfile< + default_constructible::maybe, move_constructible::maybe, + copy_constructible::maybe, move_assignable::maybe, + copy_assignable::maybe, destructible::maybe, equality_comparable::maybe, + inequality_comparable::maybe, less_than_comparable::maybe, + less_equal_comparable::maybe, greater_equal_comparable::maybe, + greater_than_comparable::yes>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasNothrowSwap, + ConformanceProfile< + default_constructible::maybe, move_constructible::maybe, + copy_constructible::maybe, move_assignable::maybe, + copy_assignable::maybe, destructible::maybe, equality_comparable::maybe, + inequality_comparable::maybe, less_than_comparable::maybe, + less_equal_comparable::maybe, greater_equal_comparable::maybe, + greater_than_comparable::maybe, swappable::nothrow>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasSwap, + ConformanceProfile< + default_constructible::maybe, move_constructible::maybe, + copy_constructible::maybe, move_assignable::maybe, + copy_assignable::maybe, destructible::maybe, equality_comparable::maybe, + inequality_comparable::maybe, less_than_comparable::maybe, + less_equal_comparable::maybe, greater_equal_comparable::maybe, + greater_than_comparable::maybe, swappable::yes>); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HasStdHashSpecialization, + ConformanceProfile< + default_constructible::maybe, move_constructible::maybe, + copy_constructible::maybe, move_assignable::maybe, + copy_assignable::maybe, destructible::maybe, equality_comparable::maybe, + inequality_comparable::maybe, less_than_comparable::maybe, + less_equal_comparable::maybe, greater_equal_comparable::maybe, + greater_than_comparable::maybe, swappable::maybe, hashable::yes>); + +//////////////////////////////////////////////////////////////////////////////// +//// The remaining aliases are combinations of the previous aliases. //// +//////////////////////////////////////////////////////////////////////////////// + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + Equatable, CombineProfiles); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + Comparable, + CombineProfiles); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + NothrowEquatable, + CombineProfiles); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + NothrowComparable, + CombineProfiles); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + Value, + CombineProfiles); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + EquatableValue, CombineProfiles); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + ComparableValue, CombineProfiles); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + DefaultConstructibleValue, + CombineProfiles); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + NothrowMoveConstructible, CombineProfiles); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + EquatableNothrowMoveConstructible, + CombineProfiles); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + ComparableNothrowMoveConstructible, + CombineProfiles); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + DefaultConstructibleNothrowMoveConstructible, + CombineProfiles); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + CopyConstructible, + CombineProfiles); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + EquatableCopyConstructible, + CombineProfiles); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + ComparableCopyConstructible, + CombineProfiles); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + DefaultConstructibleCopyConstructible, + CombineProfiles); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + NothrowMovable, + CombineProfiles); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + EquatableNothrowMovable, + CombineProfiles); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + ComparableNothrowMovable, + CombineProfiles); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + DefaultConstructibleNothrowMovable, + CombineProfiles); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + TrivialSpecialMemberFunctions, + CombineProfiles); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + TriviallyComplete, + CombineProfiles); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HashableNothrowMoveConstructible, + CombineProfiles); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HashableCopyConstructible, + CombineProfiles); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HashableNothrowMovable, + CombineProfiles); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + HashableValue, + CombineProfiles); + +ABSL_INTERNAL_PROFILE_AND_ARCHETYPE_ALIAS( + ComparableHashableValue, + CombineProfiles); + +// The "preferred" profiles that we support in Abseil. +template