diff --git a/absl/base/BUILD.bazel b/absl/base/BUILD.bazel index 1af9e45..816e592 100644 --- a/absl/base/BUILD.bazel +++ b/absl/base/BUILD.bazel
@@ -118,7 +118,6 @@ srcs = ["dynamic_annotations.cc"], hdrs = ["dynamic_annotations.h"], copts = ABSL_DEFAULT_COPTS, - defines = ["__CLANG_SUPPORT_DYN_ANNOTATION__"], linkopts = ABSL_DEFAULT_LINKOPTS, ) @@ -541,7 +540,10 @@ copts = ABSL_TEST_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, tags = ["no_test_ios_x86_64"], - deps = [":malloc_internal"], + deps = [ + ":malloc_internal", + "//absl/container:node_hash_map", + ], ) cc_test(
diff --git a/absl/base/CMakeLists.txt b/absl/base/CMakeLists.txt index a63b591..292998b 100644 --- a/absl/base/CMakeLists.txt +++ b/absl/base/CMakeLists.txt
@@ -108,8 +108,6 @@ "dynamic_annotations.cc" COPTS ${ABSL_DEFAULT_COPTS} - DEFINES - "__CLANG_SUPPORT_DYN_ANNOTATION__" PUBLIC ) @@ -497,6 +495,7 @@ ${ABSL_TEST_COPTS} DEPS absl::malloc_internal + absl::node_hash_map Threads::Threads )
diff --git a/absl/base/attributes.h b/absl/base/attributes.h index b4bb6cf..c4fd81b 100644 --- a/absl/base/attributes.h +++ b/absl/base/attributes.h
@@ -45,9 +45,9 @@ // // * `ADDRESS_SANITIZER` + `-fsanitize=address` (Clang, GCC 4.8) // * `MEMORY_SANITIZER` + `-fsanitize=memory` (Clang-only) -// * `THREAD_SANITIZER + `-fsanitize=thread` (Clang, GCC 4.8+) +// * `THREAD_SANITIZER` + `-fsanitize=thread` (Clang, GCC 4.8+) // * `UNDEFINED_BEHAVIOR_SANITIZER` + `-fsanitize=undefined` (Clang, GCC 4.9+) -// * `CONTROL_FLOW_INTEGRITY` + -fsanitize=cfi (Clang-only) +// * `CONTROL_FLOW_INTEGRITY` + `-fsanitize=cfi` (Clang-only) // // Example: //
diff --git a/absl/base/dynamic_annotations.h b/absl/base/dynamic_annotations.h index 65a54b4..2d98526 100644 --- a/absl/base/dynamic_annotations.h +++ b/absl/base/dynamic_annotations.h
@@ -140,10 +140,7 @@ #define ANNOTATE_MEMORY_IS_UNINITIALIZED(address, size) /* empty */ #endif /* DYNAMIC_ANNOTATIONS_ENABLED || MEMORY_SANITIZER */ -/* TODO(delesley) -- Replace __CLANG_SUPPORT_DYN_ANNOTATION__ with the - appropriate feature ID. */ -#if defined(__clang__) && (!defined(SWIG)) \ - && defined(__CLANG_SUPPORT_DYN_ANNOTATION__) +#if defined(__clang__) && !defined(SWIG) #if DYNAMIC_ANNOTATIONS_ENABLED == 0 #define ANNOTALYSIS_ENABLED
diff --git a/absl/base/internal/low_level_alloc_test.cc b/absl/base/internal/low_level_alloc_test.cc index 7abbbf9..2f2eaff 100644 --- a/absl/base/internal/low_level_alloc_test.cc +++ b/absl/base/internal/low_level_alloc_test.cc
@@ -21,6 +21,8 @@ #include <unordered_map> #include <utility> +#include "absl/container/node_hash_map.h" + namespace absl { ABSL_NAMESPACE_BEGIN namespace base_internal { @@ -75,7 +77,7 @@ // allocations and deallocations are reported via the MallocHook // interface. static void Test(bool use_new_arena, bool call_malloc_hook, int n) { - typedef std::unordered_map<int, BlockDesc> AllocMap; + typedef absl::node_hash_map<int, BlockDesc> AllocMap; AllocMap allocated; AllocMap::iterator it; BlockDesc block_desc;
diff --git a/absl/base/internal/spinlock.cc b/absl/base/internal/spinlock.cc index fd0c733..7cac72f 100644 --- a/absl/base/internal/spinlock.cc +++ b/absl/base/internal/spinlock.cc
@@ -66,6 +66,13 @@ submit_profile_data.Store(fn); } +// Static member variable definitions. +constexpr uint32_t SpinLock::kSpinLockHeld; +constexpr uint32_t SpinLock::kSpinLockCooperative; +constexpr uint32_t SpinLock::kSpinLockDisabledScheduling; +constexpr uint32_t SpinLock::kSpinLockSleeper; +constexpr uint32_t SpinLock::kWaitTimeMask; + // Uncommon constructors. SpinLock::SpinLock(base_internal::SchedulingMode mode) : lockword_(IsCooperative(mode) ? kSpinLockCooperative : 0) {
diff --git a/absl/base/internal/spinlock.h b/absl/base/internal/spinlock.h index 89e93aa..2b08a2d 100644 --- a/absl/base/internal/spinlock.h +++ b/absl/base/internal/spinlock.h
@@ -36,6 +36,7 @@ #include <atomic> #include "absl/base/attributes.h" +#include "absl/base/const_init.h" #include "absl/base/dynamic_annotations.h" #include "absl/base/internal/low_level_scheduling.h" #include "absl/base/internal/raw_logging.h" @@ -77,6 +78,10 @@ SpinLock(base_internal::LinkerInitialized, base_internal::SchedulingMode mode); + // Constructor for global SpinLock instances. See absl/base/const_init.h. + constexpr SpinLock(absl::ConstInitType, base_internal::SchedulingMode mode) + : lockword_(IsCooperative(mode) ? kSpinLockCooperative : 0) {} + ~SpinLock() { ABSL_TSAN_MUTEX_DESTROY(this, __tsan_mutex_not_static); } // Acquire this SpinLock.
diff --git a/absl/base/internal/sysinfo.cc b/absl/base/internal/sysinfo.cc index 72f9f17..6c69683 100644 --- a/absl/base/internal/sysinfo.cc +++ b/absl/base/internal/sysinfo.cc
@@ -53,6 +53,7 @@ #include "absl/base/internal/raw_logging.h" #include "absl/base/internal/spinlock.h" #include "absl/base/internal/unscaledcycleclock.h" +#include "absl/base/thread_annotations.h" namespace absl { ABSL_NAMESPACE_BEGIN @@ -343,15 +344,16 @@ #else // Fallback implementation of GetTID using pthread_getspecific. -static once_flag tid_once; -static pthread_key_t tid_key; -static absl::base_internal::SpinLock tid_lock( - absl::base_internal::kLinkerInitialized); +ABSL_CONST_INIT static once_flag tid_once; +ABSL_CONST_INIT static pthread_key_t tid_key; +ABSL_CONST_INIT static absl::base_internal::SpinLock tid_lock( + absl::kConstInit, base_internal::SCHEDULE_KERNEL_ONLY); // We set a bit per thread in this array to indicate that an ID is in // use. ID 0 is unused because it is the default value returned by // pthread_getspecific(). -static std::vector<uint32_t>* tid_array GUARDED_BY(tid_lock) = nullptr; +ABSL_CONST_INIT static std::vector<uint32_t> *tid_array + ABSL_GUARDED_BY(tid_lock) = nullptr; static constexpr int kBitsPerWord = 32; // tid_array is uint32_t. // Returns the TID to tid_array.
diff --git a/absl/base/internal/thread_identity_test.cc b/absl/base/internal/thread_identity_test.cc index 3685779..624d5b9 100644 --- a/absl/base/internal/thread_identity_test.cc +++ b/absl/base/internal/thread_identity_test.cc
@@ -21,6 +21,7 @@ #include "absl/base/attributes.h" #include "absl/base/internal/spinlock.h" #include "absl/base/macros.h" +#include "absl/base/thread_annotations.h" #include "absl/synchronization/internal/per_thread_sem.h" #include "absl/synchronization/mutex.h" @@ -29,10 +30,9 @@ namespace base_internal { namespace { -// protects num_identities_reused -static absl::base_internal::SpinLock map_lock( - absl::base_internal::kLinkerInitialized); -static int num_identities_reused; +ABSL_CONST_INIT static absl::base_internal::SpinLock map_lock( + absl::kConstInit, base_internal::SCHEDULE_KERNEL_ONLY); +ABSL_CONST_INIT static int num_identities_reused ABSL_GUARDED_BY(map_lock); static const void* const kCheckNoIdentity = reinterpret_cast<void*>(1); @@ -90,6 +90,7 @@ // We should have recycled ThreadIdentity objects above; while (external) // library threads allocating their own identities may preclude some // reuse, we should have sufficient repetitions to exclude this. + absl::base_internal::SpinLockHolder l(&map_lock); EXPECT_LT(kNumThreads, num_identities_reused); }
diff --git a/absl/base/spinlock_test_common.cc b/absl/base/spinlock_test_common.cc index 08f61ba..b68c51a 100644 --- a/absl/base/spinlock_test_common.cc +++ b/absl/base/spinlock_test_common.cc
@@ -56,12 +56,10 @@ static constexpr int kArrayLength = 10; static uint32_t values[kArrayLength]; -static SpinLock static_spinlock(base_internal::kLinkerInitialized); -static SpinLock static_cooperative_spinlock( - base_internal::kLinkerInitialized, - base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL); -static SpinLock static_noncooperative_spinlock( - base_internal::kLinkerInitialized, base_internal::SCHEDULE_KERNEL_ONLY); +ABSL_CONST_INIT static SpinLock static_cooperative_spinlock( + absl::kConstInit, base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL); +ABSL_CONST_INIT static SpinLock static_noncooperative_spinlock( + absl::kConstInit, base_internal::SCHEDULE_KERNEL_ONLY); // Simple integer hash function based on the public domain lookup2 hash. // http://burtleburtle.net/bob/c/lookup2.c @@ -191,10 +189,6 @@ EXPECT_GT(expected_max_value_decoded, before_max_value_decoded); } -TEST(SpinLockWithThreads, StaticSpinLock) { - ThreadedTest(&static_spinlock); -} - TEST(SpinLockWithThreads, StackSpinLock) { SpinLock spinlock; ThreadedTest(&spinlock);
diff --git a/absl/container/internal/container_memory.h b/absl/container/internal/container_memory.h index 3487ac1..536ea39 100644 --- a/absl/container/internal/container_memory.h +++ b/absl/container/internal/container_memory.h
@@ -250,8 +250,8 @@ // type, which is non-portable. template <class Pair, class = std::true_type> struct OffsetOf { - static constexpr size_t kFirst = -1; - static constexpr size_t kSecond = -1; + static constexpr size_t kFirst = static_cast<size_t>(-1); + static constexpr size_t kSecond = static_cast<size_t>(-1); }; template <class Pair>
diff --git a/absl/container/node_hash_map.h b/absl/container/node_hash_map.h index fccea18..174b971 100644 --- a/absl/container/node_hash_map.h +++ b/absl/container/node_hash_map.h
@@ -514,12 +514,6 @@ // // Returns the function used for comparing keys equality. using Base::key_eq; - - ABSL_DEPRECATED("Call `hash_function()` instead.") - typename Base::hasher hash_funct() { return this->hash_function(); } - - ABSL_DEPRECATED("Call `rehash()` instead.") - void resize(typename Base::size_type hint) { this->rehash(hint); } }; // erase_if(node_hash_map<>, Pred)
diff --git a/absl/container/node_hash_set.h b/absl/container/node_hash_set.h index ad54b6d..56bab5c 100644 --- a/absl/container/node_hash_set.h +++ b/absl/container/node_hash_set.h
@@ -427,12 +427,6 @@ // // Returns the function used for comparing keys equality. using Base::key_eq; - - ABSL_DEPRECATED("Call `hash_function()` instead.") - typename Base::hasher hash_funct() { return this->hash_function(); } - - ABSL_DEPRECATED("Call `rehash()` instead.") - void resize(typename Base::size_type hint) { this->rehash(hint); } }; // erase_if(node_hash_set<>, Pred)
diff --git a/absl/debugging/internal/stacktrace_arm-inl.inc b/absl/debugging/internal/stacktrace_arm-inl.inc index fffda96..2a1bf2e 100644 --- a/absl/debugging/internal/stacktrace_arm-inl.inc +++ b/absl/debugging/internal/stacktrace_arm-inl.inc
@@ -1,9 +1,18 @@ -// Copyright 2011 and onwards Google Inc. -// All rights reserved. +// Copyright 2017 The Abseil Authors. // -// Author: Doug Kwan +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// // This is inspired by Craig Silverstein's PowerPC stacktrace code. -// #ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_ARM_INL_H_ #define ABSL_DEBUGGING_INTERNAL_STACKTRACE_ARM_INL_H_
diff --git a/absl/debugging/internal/stacktrace_generic-inl.inc b/absl/debugging/internal/stacktrace_generic-inl.inc index ac034c9..b2792a1 100644 --- a/absl/debugging/internal/stacktrace_generic-inl.inc +++ b/absl/debugging/internal/stacktrace_generic-inl.inc
@@ -1,7 +1,16 @@ -// Copyright 2000 - 2007 Google Inc. -// All rights reserved. +// Copyright 2017 The Abseil Authors. // -// Author: Sanjay Ghemawat +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. // // Portable implementation - just use glibc //
diff --git a/absl/debugging/symbolize_elf.inc b/absl/debugging/symbolize_elf.inc index fe1d36e..ec86f9a 100644 --- a/absl/debugging/symbolize_elf.inc +++ b/absl/debugging/symbolize_elf.inc
@@ -149,13 +149,15 @@ // Moreover, we are using only TryLock(), if the decorator list // is being modified (is busy), we skip all decorators, and possibly // loose some info. Sorry, that's the best we could do. -base_internal::SpinLock g_decorators_mu(base_internal::kLinkerInitialized); +ABSL_CONST_INIT absl::base_internal::SpinLock g_decorators_mu( + absl::kConstInit, absl::base_internal::SCHEDULE_KERNEL_ONLY); const int kMaxFileMappingHints = 8; int g_num_file_mapping_hints; FileMappingHint g_file_mapping_hints[kMaxFileMappingHints]; // Protects g_file_mapping_hints. -base_internal::SpinLock g_file_mapping_mu(base_internal::kLinkerInitialized); +ABSL_CONST_INIT absl::base_internal::SpinLock g_file_mapping_mu( + absl::kConstInit, absl::base_internal::SCHEDULE_KERNEL_ONLY); // Async-signal-safe function to zero a buffer. // memset() is not guaranteed to be async-signal-safe.
diff --git a/absl/flags/BUILD.bazel b/absl/flags/BUILD.bazel index 685e395..3681082 100644 --- a/absl/flags/BUILD.bazel +++ b/absl/flags/BUILD.bazel
@@ -40,6 +40,7 @@ deps = [ ":config", ":handle", + ":marshalling", ":registry", "//absl/base", "//absl/base:config", @@ -143,8 +144,6 @@ "//absl/flags:__pkg__", ], deps = [ - ":config", - ":marshalling", "//absl/base:config", "//absl/base:core_headers", "//absl/base:fast_type_id", @@ -154,6 +153,22 @@ ) cc_library( + name = "private_handle_accessor", + srcs = [ + "internal/private_handle_accessor.cc", + ], + hdrs = [ + "internal/private_handle_accessor.h", + ], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = [ + "//absl/flags:__pkg__", + ], + deps = [":handle"], +) + +cc_library( name = "registry", srcs = [ "internal/registry.cc", @@ -171,6 +186,7 @@ deps = [ ":config", ":handle", + ":private_handle_accessor", "//absl/base:config", "//absl/base:core_headers", "//absl/base:raw_logging_internal", @@ -222,6 +238,7 @@ ":flag_internal", ":handle", ":path_util", + ":private_handle_accessor", ":program_name", ":registry", "//absl/base:config", @@ -263,6 +280,7 @@ ":flag", ":flag_internal", ":handle", + ":private_handle_accessor", ":program_name", ":registry", ":usage", @@ -289,6 +307,7 @@ ":config", ":flag", ":handle", + ":private_handle_accessor", ":registry", "//absl/memory", "//absl/strings",
diff --git a/absl/flags/CMakeLists.txt b/absl/flags/CMakeLists.txt index ec82ee1..e6b17c9 100644 --- a/absl/flags/CMakeLists.txt +++ b/absl/flags/CMakeLists.txt
@@ -31,6 +31,7 @@ absl::config absl::flags_config absl::flags_handle + absl::flags_marshalling absl::flags_registry absl::synchronization absl::meta @@ -129,8 +130,6 @@ DEPS absl::config absl::fast_type_id - absl::flags_config - absl::flags_marshalling absl::core_headers absl::optional absl::raw_logging_internal @@ -141,6 +140,22 @@ # Internal-only target, do not depend on directly. absl_cc_library( NAME + flags_private_handle_accessor + SRCS + "internal/private_handle_accessor.cc" + HDRS + "internal/private_handle_accessor.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::flags_handle +) + +# Internal-only target, do not depend on directly. +absl_cc_library( + NAME flags_registry SRCS "internal/registry.cc" @@ -156,6 +171,7 @@ absl::config absl::flags_config absl::flags_handle + absl::flags_private_handle_accessor absl::core_headers absl::raw_logging_internal absl::strings @@ -203,6 +219,7 @@ absl::flags_config absl::flags absl::flags_handle + absl::flags_private_handle_accessor absl::flags_internal absl::flags_path_util absl::flags_program_name @@ -248,6 +265,7 @@ absl::flags_config absl::flags absl::flags_handle + absl::flags_private_handle_accessor absl::flags_internal absl::flags_program_name absl::flags_registry @@ -270,6 +288,7 @@ absl::flags absl::flags_config absl::flags_handle + absl::flags_private_handle_accessor absl::flags_registry absl::memory absl::strings
diff --git a/absl/flags/flag.h b/absl/flags/flag.h index 194a9d3..8dd1b9b 100644 --- a/absl/flags/flag.h +++ b/absl/flags/flag.h
@@ -118,11 +118,12 @@ return impl_; } - impl_ = - new flags_internal::Flag<T>(name_, filename_, - {flags_internal::FlagHelpMsg(help_gen_), - flags_internal::FlagHelpKind::kGenFunc}, - default_value_gen_); + impl_ = new flags_internal::Flag<T>( + name_, filename_, + {flags_internal::FlagHelpMsg(help_gen_), + flags_internal::FlagHelpKind::kGenFunc}, + {flags_internal::FlagDefaultSrc(default_value_gen_), + flags_internal::FlagDefaultKind::kGenFunc}); inited_.store(true, std::memory_order_release); } @@ -132,14 +133,12 @@ // Public methods of `absl::Flag<T>` are NOT part of the Abseil Flags API. // See https://abseil.io/docs/cpp/guides/flags bool IsRetired() const { return GetImpl()->IsRetired(); } - bool IsAbseilFlag() const { return GetImpl()->IsAbseilFlag(); } absl::string_view Name() const { return GetImpl()->Name(); } std::string Help() const { return GetImpl()->Help(); } bool IsModified() const { return GetImpl()->IsModified(); } bool IsSpecifiedOnCommandLine() const { return GetImpl()->IsSpecifiedOnCommandLine(); } - absl::string_view Typename() const { return GetImpl()->Typename(); } std::string Filename() const { return GetImpl()->Filename(); } std::string DefaultValue() const { return GetImpl()->DefaultValue(); } std::string CurrentValue() const { return GetImpl()->CurrentValue(); } @@ -311,9 +310,12 @@ static std::string NonConst() { return ABSL_FLAG_IMPL_FLAGHELP(txt); } \ } -#define ABSL_FLAG_IMPL_DECLARE_DEF_VAL_WRAPPER(name, Type, default_value) \ - static void AbslFlagsInitFlag##name(void* dst) { \ - absl::flags_internal::MakeFromDefaultValue<Type>(dst, default_value); \ +#define ABSL_FLAG_IMPL_DECLARE_DEF_VAL_WRAPPER(name, Type, default_value) \ + struct AbslFlagDefaultGenFor##name { \ + Type value = absl::flags_internal::InitDefaultValue<Type>(default_value); \ + static void Gen(void* p) { \ + new (p) Type(AbslFlagDefaultGenFor##name{}.value); \ + } \ } // ABSL_FLAG_IMPL @@ -322,29 +324,30 @@ // global name for FLAGS_no<flag_name> symbol, thus preventing the possibility // of defining two flags with names foo and nofoo. #if !defined(_MSC_VER) || defined(__clang__) -#define ABSL_FLAG_IMPL(Type, name, default_value, help) \ - namespace absl /* block flags in namespaces */ {} \ - ABSL_FLAG_IMPL_DECLARE_DEF_VAL_WRAPPER(name, Type, default_value) \ - ABSL_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, help); \ - ABSL_CONST_INIT absl::Flag<Type> FLAGS_##name{ \ - ABSL_FLAG_IMPL_FLAGNAME(#name), ABSL_FLAG_IMPL_FILENAME(), \ - absl::flags_internal::HelpArg<AbslFlagHelpGenFor##name>(0), \ - &AbslFlagsInitFlag##name}; \ - extern absl::flags_internal::FlagRegistrarEmpty FLAGS_no##name; \ - absl::flags_internal::FlagRegistrarEmpty FLAGS_no##name = \ + +#define ABSL_FLAG_IMPL(Type, name, default_value, help) \ + namespace absl /* block flags in namespaces */ {} \ + ABSL_FLAG_IMPL_DECLARE_DEF_VAL_WRAPPER(name, Type, default_value); \ + ABSL_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, help); \ + ABSL_CONST_INIT absl::Flag<Type> FLAGS_##name{ \ + ABSL_FLAG_IMPL_FLAGNAME(#name), ABSL_FLAG_IMPL_FILENAME(), \ + absl::flags_internal::HelpArg<AbslFlagHelpGenFor##name>(0), \ + absl::flags_internal::DefaultArg<Type, AbslFlagDefaultGenFor##name>(0)}; \ + extern absl::flags_internal::FlagRegistrarEmpty FLAGS_no##name; \ + absl::flags_internal::FlagRegistrarEmpty FLAGS_no##name = \ ABSL_FLAG_IMPL_REGISTRAR(Type, FLAGS_##name) #else // MSVC version uses aggregate initialization. We also do not try to // optimize away help wrapper. -#define ABSL_FLAG_IMPL(Type, name, default_value, help) \ - namespace absl /* block flags in namespaces */ {} \ - ABSL_FLAG_IMPL_DECLARE_DEF_VAL_WRAPPER(name, Type, default_value) \ - ABSL_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, help); \ - ABSL_CONST_INIT absl::Flag<Type> FLAGS_##name{ \ - ABSL_FLAG_IMPL_FLAGNAME(#name), ABSL_FLAG_IMPL_FILENAME(), \ - &AbslFlagHelpGenFor##name::NonConst, &AbslFlagsInitFlag##name}; \ - extern absl::flags_internal::FlagRegistrarEmpty FLAGS_no##name; \ - absl::flags_internal::FlagRegistrarEmpty FLAGS_no##name = \ +#define ABSL_FLAG_IMPL(Type, name, default_value, help) \ + namespace absl /* block flags in namespaces */ {} \ + ABSL_FLAG_IMPL_DECLARE_DEF_VAL_WRAPPER(name, Type, default_value); \ + ABSL_FLAG_IMPL_DECLARE_HELP_WRAPPER(name, help); \ + ABSL_CONST_INIT absl::Flag<Type> FLAGS_##name{ \ + ABSL_FLAG_IMPL_FLAGNAME(#name), ABSL_FLAG_IMPL_FILENAME(), \ + &AbslFlagHelpGenFor##name::NonConst, &AbslFlagDefaultGenFor##name::Gen}; \ + extern absl::flags_internal::FlagRegistrarEmpty FLAGS_no##name; \ + absl::flags_internal::FlagRegistrarEmpty 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 6fa178f..015b1fc 100644 --- a/absl/flags/flag_test.cc +++ b/absl/flags/flag_test.cc
@@ -128,26 +128,27 @@ using String = std::string; -#define DEFINE_CONSTRUCTED_FLAG(T) \ - constexpr flags::Flag<T> f1##T("f1", "file", help_arg, &TestMakeDflt<T>); \ - ABSL_CONST_INIT flags::Flag<T> f2##T( \ - "f2", "file", \ - {flags::FlagHelpMsg(&TestHelpMsg), flags::FlagHelpKind::kGenFunc}, \ - &TestMakeDflt<T>) +#define DEFINE_CONSTRUCTED_FLAG(T, dflt, dflt_kind) \ + constexpr flags::FlagDefaultArg f1default##T{ \ + flags::FlagDefaultSrc{dflt}, flags::FlagDefaultKind::dflt_kind}; \ + constexpr flags::Flag<T> f1##T("f1", "file", help_arg, f1default##T); \ + ABSL_CONST_INIT flags::Flag<T> f2##T( \ + "f2", "file", \ + {flags::FlagHelpMsg(&TestHelpMsg), flags::FlagHelpKind::kGenFunc}, \ + flags::FlagDefaultArg{flags::FlagDefaultSrc(&TestMakeDflt<T>), \ + flags::FlagDefaultKind::kGenFunc}) -#define TEST_CONSTRUCTED_FLAG(T) TestConstructionFor(f1##T, &f2##T); - -DEFINE_CONSTRUCTED_FLAG(bool); -DEFINE_CONSTRUCTED_FLAG(int16_t); -DEFINE_CONSTRUCTED_FLAG(uint16_t); -DEFINE_CONSTRUCTED_FLAG(int32_t); -DEFINE_CONSTRUCTED_FLAG(uint32_t); -DEFINE_CONSTRUCTED_FLAG(int64_t); -DEFINE_CONSTRUCTED_FLAG(uint64_t); -DEFINE_CONSTRUCTED_FLAG(float); -DEFINE_CONSTRUCTED_FLAG(double); -DEFINE_CONSTRUCTED_FLAG(String); -DEFINE_CONSTRUCTED_FLAG(UDT); +DEFINE_CONSTRUCTED_FLAG(bool, true, kOneWord); +DEFINE_CONSTRUCTED_FLAG(int16_t, 1, kOneWord); +DEFINE_CONSTRUCTED_FLAG(uint16_t, 2, kOneWord); +DEFINE_CONSTRUCTED_FLAG(int32_t, 3, kOneWord); +DEFINE_CONSTRUCTED_FLAG(uint32_t, 4, kOneWord); +DEFINE_CONSTRUCTED_FLAG(int64_t, 5, kOneWord); +DEFINE_CONSTRUCTED_FLAG(uint64_t, 6, kOneWord); +DEFINE_CONSTRUCTED_FLAG(float, 7.8, kFloat); +DEFINE_CONSTRUCTED_FLAG(double, 9.10, kDouble); +DEFINE_CONSTRUCTED_FLAG(String, &TestMakeDflt<String>, kGenFunc); +DEFINE_CONSTRUCTED_FLAG(UDT, &TestMakeDflt<UDT>, kGenFunc); template <typename T> bool TestConstructionFor(const flags::Flag<T>& f1, flags::Flag<T>* f2) { @@ -164,6 +165,8 @@ return true; } +#define TEST_CONSTRUCTED_FLAG(T) TestConstructionFor(f1##T, &f2##T); + TEST_F(FlagTest, TestConstruction) { TEST_CONSTRUCTED_FLAG(bool); TEST_CONSTRUCTED_FLAG(int16_t); @@ -443,29 +446,29 @@ TEST_F(FlagTest, TestGetViaReflection) { auto* handle = flags::FindCommandLineFlag("test_flag_01"); - EXPECT_EQ(*handle->Get<bool>(), true); + EXPECT_EQ(*handle->TryGet<bool>(), true); handle = flags::FindCommandLineFlag("test_flag_02"); - EXPECT_EQ(*handle->Get<int>(), 1234); + EXPECT_EQ(*handle->TryGet<int>(), 1234); handle = flags::FindCommandLineFlag("test_flag_03"); - EXPECT_EQ(*handle->Get<int16_t>(), -34); + EXPECT_EQ(*handle->TryGet<int16_t>(), -34); handle = flags::FindCommandLineFlag("test_flag_04"); - EXPECT_EQ(*handle->Get<uint16_t>(), 189); + EXPECT_EQ(*handle->TryGet<uint16_t>(), 189); handle = flags::FindCommandLineFlag("test_flag_05"); - EXPECT_EQ(*handle->Get<int32_t>(), 10765); + EXPECT_EQ(*handle->TryGet<int32_t>(), 10765); handle = flags::FindCommandLineFlag("test_flag_06"); - EXPECT_EQ(*handle->Get<uint32_t>(), 40000); + EXPECT_EQ(*handle->TryGet<uint32_t>(), 40000); handle = flags::FindCommandLineFlag("test_flag_07"); - EXPECT_EQ(*handle->Get<int64_t>(), -1234567); + EXPECT_EQ(*handle->TryGet<int64_t>(), -1234567); handle = flags::FindCommandLineFlag("test_flag_08"); - EXPECT_EQ(*handle->Get<uint64_t>(), 9876543); + EXPECT_EQ(*handle->TryGet<uint64_t>(), 9876543); handle = flags::FindCommandLineFlag("test_flag_09"); - EXPECT_NEAR(*handle->Get<double>(), -9.876e-50, 1e-55); + EXPECT_NEAR(*handle->TryGet<double>(), -9.876e-50, 1e-55); handle = flags::FindCommandLineFlag("test_flag_10"); - EXPECT_NEAR(*handle->Get<float>(), 1.234e12f, 1e5f); + EXPECT_NEAR(*handle->TryGet<float>(), 1.234e12f, 1e5f); handle = flags::FindCommandLineFlag("test_flag_11"); - EXPECT_EQ(*handle->Get<std::string>(), ""); + EXPECT_EQ(*handle->TryGet<std::string>(), ""); handle = flags::FindCommandLineFlag("test_flag_12"); - EXPECT_EQ(*handle->Get<absl::Duration>(), absl::Minutes(10)); + EXPECT_EQ(*handle->TryGet<absl::Duration>(), absl::Minutes(10)); } // --------------------------------------------------------------------
diff --git a/absl/flags/internal/commandlineflag.cc b/absl/flags/internal/commandlineflag.cc index 90765a3..8411243 100644 --- a/absl/flags/internal/commandlineflag.cc +++ b/absl/flags/internal/commandlineflag.cc
@@ -22,7 +22,11 @@ FlagStateInterface::~FlagStateInterface() {} bool CommandLineFlag::IsRetired() const { return false; } -bool CommandLineFlag::IsAbseilFlag() const { return true; } + +bool CommandLineFlag::ParseFrom(absl::string_view value, std::string* error) { + return ParseFrom(value, flags_internal::SET_FLAGS_VALUE, + flags_internal::kProgrammaticChange, error); +} } // namespace flags_internal ABSL_NAMESPACE_END
diff --git a/absl/flags/internal/commandlineflag.h b/absl/flags/internal/commandlineflag.h index ef992f7..2d3b794 100644 --- a/absl/flags/internal/commandlineflag.h +++ b/absl/flags/internal/commandlineflag.h
@@ -16,18 +16,12 @@ #ifndef ABSL_FLAGS_INTERNAL_COMMANDLINEFLAG_H_ #define ABSL_FLAGS_INTERNAL_COMMANDLINEFLAG_H_ -#include <stddef.h> -#include <stdint.h> - #include <memory> #include <string> -#include <typeinfo> #include "absl/base/config.h" #include "absl/base/internal/fast_type_id.h" #include "absl/base/macros.h" -#include "absl/flags/config.h" -#include "absl/flags/marshalling.h" #include "absl/strings/string_view.h" #include "absl/types/optional.h" @@ -93,7 +87,7 @@ // Attempts to retrieve the flag value. Returns value on success, // absl::nullopt otherwise. template <typename T> - absl::optional<T> Get() const { + absl::optional<T> TryGet() const { if (IsRetired() || !IsOfType<T>()) { return absl::nullopt; } @@ -130,28 +124,24 @@ virtual absl::string_view Name() const = 0; // Returns name of the file where this flag is defined. virtual std::string Filename() const = 0; - // Returns name of the flag's value type for some built-in types or empty - // string. - virtual absl::string_view Typename() const = 0; // Returns help message associated with this flag. virtual std::string Help() const = 0; // Returns true iff this object corresponds to retired flag. virtual bool IsRetired() const; - // Returns true iff this is a handle to an Abseil Flag. - virtual bool IsAbseilFlag() const; - // Returns id of the flag's value type. - virtual FlagFastTypeId 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; + // 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 `error` + // to indicate the error, leaves the flag unchanged, and returns false. + bool ParseFrom(absl::string_view value, std::string* error); - // Interface to save flag to some persistent state. Returns current flag state - // or nullptr if flag does not support saving and restoring a state. - virtual std::unique_ptr<FlagStateInterface> SaveState() = 0; + protected: + ~CommandLineFlag() = default; + + private: + friend class PrivateHandleAccessor; // 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 `error` @@ -166,17 +156,23 @@ flags_internal::ValueSource source, std::string* error) = 0; - // Checks that flags default value can be converted to string and back to the - // flag's value type. - virtual void CheckDefaultValueParsingRoundtrip() const = 0; + // Returns id of the flag's value type. + virtual FlagFastTypeId TypeId() const = 0; - protected: - ~CommandLineFlag() = default; + // Interface to save flag to some persistent state. Returns current flag state + // or nullptr if flag does not support saving and restoring a state. + virtual std::unique_ptr<FlagStateInterface> SaveState() = 0; - 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; + + // Validates supplied value usign validator or parseflag routine + virtual bool ValidateInputValue(absl::string_view value) const = 0; + + // Checks that flags default value can be converted to string and back to the + // flag's value type. + virtual void CheckDefaultValueParsingRoundtrip() const = 0; }; // This macro is the "source of truth" for the list of supported flag built-in
diff --git a/absl/flags/internal/commandlineflag_test.cc b/absl/flags/internal/commandlineflag_test.cc index c1142b7..54d50ff 100644 --- a/absl/flags/internal/commandlineflag_test.cc +++ b/absl/flags/internal/commandlineflag_test.cc
@@ -20,6 +20,7 @@ #include "gtest/gtest.h" #include "absl/flags/flag.h" +#include "absl/flags/internal/private_handle_accessor.h" #include "absl/flags/internal/registry.h" #include "absl/flags/usage_config.h" #include "absl/memory/memory.h" @@ -67,7 +68,6 @@ ASSERT_TRUE(flag_01); EXPECT_EQ(flag_01->Name(), "int_flag"); EXPECT_EQ(flag_01->Help(), "int_flag help"); - EXPECT_EQ(flag_01->Typename(), ""); EXPECT_TRUE(!flag_01->IsRetired()); EXPECT_TRUE(flag_01->IsOfType<int>()); EXPECT_TRUE( @@ -80,7 +80,6 @@ ASSERT_TRUE(flag_02); EXPECT_EQ(flag_02->Name(), "string_flag"); EXPECT_EQ(flag_02->Help(), "string_flag help"); - EXPECT_EQ(flag_02->Typename(), ""); EXPECT_TRUE(!flag_02->IsRetired()); EXPECT_TRUE(flag_02->IsOfType<std::string>()); EXPECT_TRUE( @@ -93,7 +92,6 @@ ASSERT_TRUE(flag_03); EXPECT_EQ(flag_03->Name(), "bool_retired_flag"); EXPECT_EQ(flag_03->Help(), ""); - EXPECT_EQ(flag_03->Typename(), ""); EXPECT_TRUE(flag_03->IsRetired()); EXPECT_TRUE(flag_03->IsOfType<bool>()); EXPECT_EQ(flag_03->Filename(), "RETIRED"); @@ -125,49 +123,53 @@ auto* flag_01 = flags::FindCommandLineFlag("int_flag"); EXPECT_FALSE(flag_01->IsSpecifiedOnCommandLine()); - EXPECT_TRUE(flag_01->ParseFrom("11", flags::SET_FLAGS_VALUE, - flags::kProgrammaticChange, &err)); + EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom( + flag_01, "11", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange, &err)); EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 11); EXPECT_FALSE(flag_01->IsSpecifiedOnCommandLine()); - EXPECT_TRUE(flag_01->ParseFrom("-123", flags::SET_FLAGS_VALUE, - flags::kProgrammaticChange, &err)); + EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom( + flag_01, "-123", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange, + &err)); EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), -123); EXPECT_FALSE(flag_01->IsSpecifiedOnCommandLine()); - EXPECT_TRUE(!flag_01->ParseFrom("xyz", flags::SET_FLAGS_VALUE, - flags::kProgrammaticChange, &err)); + EXPECT_TRUE(!flags::PrivateHandleAccessor::ParseFrom( + flag_01, "xyz", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange, + &err)); EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), -123); EXPECT_EQ(err, "Illegal value 'xyz' specified for flag 'int_flag'"); EXPECT_FALSE(flag_01->IsSpecifiedOnCommandLine()); - EXPECT_TRUE(!flag_01->ParseFrom("A1", flags::SET_FLAGS_VALUE, - flags::kProgrammaticChange, &err)); + EXPECT_TRUE(!flags::PrivateHandleAccessor::ParseFrom( + flag_01, "A1", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange, &err)); EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), -123); EXPECT_EQ(err, "Illegal value 'A1' specified for flag 'int_flag'"); EXPECT_FALSE(flag_01->IsSpecifiedOnCommandLine()); - EXPECT_TRUE(flag_01->ParseFrom("0x10", flags::SET_FLAGS_VALUE, - flags::kProgrammaticChange, &err)); + EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom( + flag_01, "0x10", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange, + &err)); EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 16); EXPECT_FALSE(flag_01->IsSpecifiedOnCommandLine()); - EXPECT_TRUE(flag_01->ParseFrom("011", flags::SET_FLAGS_VALUE, - flags::kCommandLine, &err)); + EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom( + flag_01, "011", flags::SET_FLAGS_VALUE, flags::kCommandLine, &err)); EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 11); EXPECT_TRUE(flag_01->IsSpecifiedOnCommandLine()); - EXPECT_TRUE(!flag_01->ParseFrom("", flags::SET_FLAGS_VALUE, - flags::kProgrammaticChange, &err)); + EXPECT_TRUE(!flags::PrivateHandleAccessor::ParseFrom( + flag_01, "", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange, &err)); EXPECT_EQ(err, "Illegal value '' specified for flag 'int_flag'"); auto* flag_02 = flags::FindCommandLineFlag("string_flag"); - EXPECT_TRUE(flag_02->ParseFrom("xyz", flags::SET_FLAGS_VALUE, - flags::kProgrammaticChange, &err)); + EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom( + flag_02, "xyz", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange, + &err)); EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), "xyz"); - EXPECT_TRUE(flag_02->ParseFrom("", flags::SET_FLAGS_VALUE, - flags::kProgrammaticChange, &err)); + EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom( + flag_02, "", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange, &err)); EXPECT_EQ(absl::GetFlag(FLAGS_string_flag), ""); } @@ -178,14 +180,16 @@ auto* flag_01 = flags::FindCommandLineFlag("int_flag"); - EXPECT_TRUE(flag_01->ParseFrom("111", flags::SET_FLAGS_DEFAULT, - flags::kProgrammaticChange, &err)); + EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom( + flag_01, "111", flags::SET_FLAGS_DEFAULT, flags::kProgrammaticChange, + &err)); EXPECT_EQ(flag_01->DefaultValue(), "111"); auto* flag_02 = flags::FindCommandLineFlag("string_flag"); - EXPECT_TRUE(flag_02->ParseFrom("abc", flags::SET_FLAGS_DEFAULT, - flags::kProgrammaticChange, &err)); + EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom( + flag_02, "abc", flags::SET_FLAGS_DEFAULT, flags::kProgrammaticChange, + &err)); EXPECT_EQ(flag_02->DefaultValue(), "abc"); } @@ -196,22 +200,26 @@ auto* flag_01 = flags::FindCommandLineFlag("int_flag"); - EXPECT_TRUE(flag_01->ParseFrom("22", flags::SET_FLAG_IF_DEFAULT, - flags::kProgrammaticChange, &err)) + EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom( + flag_01, "22", flags::SET_FLAG_IF_DEFAULT, flags::kProgrammaticChange, + &err)) << err; EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 22); - EXPECT_TRUE(flag_01->ParseFrom("33", flags::SET_FLAG_IF_DEFAULT, - flags::kProgrammaticChange, &err)); + EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom( + flag_01, "33", flags::SET_FLAG_IF_DEFAULT, flags::kProgrammaticChange, + &err)); EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 22); // EXPECT_EQ(err, "ERROR: int_flag is already set to 22"); // Reset back to default value - EXPECT_TRUE(flag_01->ParseFrom("201", flags::SET_FLAGS_VALUE, - flags::kProgrammaticChange, &err)); + EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom( + flag_01, "201", flags::SET_FLAGS_VALUE, flags::kProgrammaticChange, + &err)); - EXPECT_TRUE(flag_01->ParseFrom("33", flags::SET_FLAG_IF_DEFAULT, - flags::kProgrammaticChange, &err)); + EXPECT_TRUE(flags::PrivateHandleAccessor::ParseFrom( + flag_01, "33", flags::SET_FLAG_IF_DEFAULT, flags::kProgrammaticChange, + &err)); EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 201); // EXPECT_EQ(err, "ERROR: int_flag is already set to 201"); }
diff --git a/absl/flags/internal/flag.cc b/absl/flags/internal/flag.cc index 089567f..8f0777f 100644 --- a/absl/flags/internal/flag.cc +++ b/absl/flags/internal/flag.cc
@@ -139,19 +139,43 @@ void FlagImpl::Init() { new (&data_guard_) absl::Mutex; - // At this point the default_value_ always points to gen_func. + auto def_kind = static_cast<FlagDefaultKind>(def_kind_); + switch (ValueStorageKind()) { case FlagValueStorageKind::kAlignedBuffer: + // For this storage kind the default_value_ always points to gen_func + // during initialization. + assert(def_kind == FlagDefaultKind::kGenFunc); (*default_value_.gen_func)(AlignedBufferValue()); break; case FlagValueStorageKind::kOneWordAtomic: { alignas(int64_t) std::array<char, sizeof(int64_t)> buf{}; - (*default_value_.gen_func)(buf.data()); - auto value = absl::bit_cast<int64_t>(buf); - OneWordValue().store(value, std::memory_order_release); + switch (def_kind) { + case FlagDefaultKind::kOneWord: + std::memcpy(buf.data(), &default_value_.one_word, + sizeof(default_value_.one_word)); + break; + case FlagDefaultKind::kFloat: + std::memcpy(buf.data(), &default_value_.float_value, + sizeof(default_value_.float_value)); + break; + case FlagDefaultKind::kDouble: + std::memcpy(buf.data(), &default_value_.double_value, + sizeof(default_value_.double_value)); + break; + default: + assert(def_kind == FlagDefaultKind::kGenFunc); + (*default_value_.gen_func)(buf.data()); + break; + } + OneWordValue().store(absl::bit_cast<int64_t>(buf), + std::memory_order_release); break; } case FlagValueStorageKind::kTwoWordsAtomic: { + // For this storage kind the default_value_ always points to gen_func + // during initialization. + assert(def_kind == FlagDefaultKind::kGenFunc); alignas(AlignedTwoWords) std::array<char, sizeof(AlignedTwoWords)> buf{}; (*default_value_.gen_func)(buf.data()); auto atomic_value = absl::bit_cast<AlignedTwoWords>(buf); @@ -196,11 +220,23 @@ std::unique_ptr<void, DynValueDeleter> FlagImpl::MakeInitValue() const { void* res = nullptr; - if (DefaultKind() == FlagDefaultKind::kDynamicValue) { - res = flags_internal::Clone(op_, default_value_.dynamic_value); - } else { - res = flags_internal::Alloc(op_); - (*default_value_.gen_func)(res); + switch (DefaultKind()) { + case FlagDefaultKind::kDynamicValue: + res = flags_internal::Clone(op_, default_value_.dynamic_value); + break; + case FlagDefaultKind::kGenFunc: + res = flags_internal::Alloc(op_); + (*default_value_.gen_func)(res); + break; + case FlagDefaultKind::kOneWord: + res = flags_internal::Clone(op_, &default_value_.one_word); + break; + case FlagDefaultKind::kFloat: + res = flags_internal::Clone(op_, &default_value_.float_value); + break; + case FlagDefaultKind::kDouble: + res = flags_internal::Clone(op_, &default_value_.double_value); + break; } return {res, DynValueDeleter{op_}}; } @@ -235,8 +271,6 @@ return flags_internal::GetUsageConfig().normalize_filename(filename_); } -absl::string_view FlagImpl::Typename() const { return ""; } - std::string FlagImpl::Help() const { return HelpSourceKind() == FlagHelpKind::kLiteral ? help_.literal : help_.gen_func(); @@ -246,11 +280,6 @@ return flags_internal::FastTypeId(op_); } -bool FlagImpl::IsModified() const { - absl::MutexLock l(DataGuard()); - return modified_; -} - bool FlagImpl::IsSpecifiedOnCommandLine() const { absl::MutexLock l(DataGuard()); return on_command_line_;
diff --git a/absl/flags/internal/flag.h b/absl/flags/internal/flag.h index 6da25aa..f53f484 100644 --- a/absl/flags/internal/flag.h +++ b/absl/flags/internal/flag.h
@@ -31,6 +31,7 @@ #include "absl/flags/config.h" #include "absl/flags/internal/commandlineflag.h" #include "absl/flags/internal/registry.h" +#include "absl/flags/marshalling.h" #include "absl/memory/memory.h" #include "absl/meta/type_traits.h" #include "absl/strings/str_cat.h" @@ -206,12 +207,72 @@ union FlagDefaultSrc { constexpr explicit FlagDefaultSrc(FlagDfltGenFunc gen_func_arg) : gen_func(gen_func_arg) {} + template <typename T> + constexpr explicit FlagDefaultSrc(T one_word_value) + : one_word(static_cast<int64_t>(one_word_value)) {} + constexpr explicit FlagDefaultSrc(float f) : float_value(f) {} + constexpr explicit FlagDefaultSrc(double d) : double_value(d) {} void* dynamic_value; FlagDfltGenFunc gen_func; + int64_t one_word; + float float_value; + double double_value; }; -enum class FlagDefaultKind : uint8_t { kDynamicValue = 0, kGenFunc = 1 }; +enum class FlagDefaultKind : uint8_t { + kDynamicValue = 0, + kGenFunc = 1, + kOneWord = 2, + kFloat = 3, + kDouble = 4 +}; + +struct FlagDefaultArg { + FlagDefaultSrc source; + FlagDefaultKind kind; +}; + +// This struct and corresponding overload to InitDefaultValue are used to +// facilitate usage of {} as default value in ABSL_FLAG macro. +// TODO(rogeeff): Fix handling types with explicit constructors. +struct EmptyBraces {}; + +template <typename T> +constexpr T InitDefaultValue(T t) { + return t; +} + +template <typename T> +constexpr T InitDefaultValue(EmptyBraces) { + return T{}; +} + +template <typename ValueT, typename GenT, + typename std::enable_if<std::is_integral<ValueT>::value, int>::type = + (GenT{}, 0)> +constexpr FlagDefaultArg DefaultArg(int) { + return {FlagDefaultSrc(GenT{}.value), FlagDefaultKind::kOneWord}; +} + +template <typename ValueT, typename GenT, + typename std::enable_if<std::is_same<ValueT, float>::value, + int>::type = (GenT{}, 0)> +constexpr FlagDefaultArg DefaultArg(int) { + return {FlagDefaultSrc(GenT{}.value), FlagDefaultKind::kFloat}; +} + +template <typename ValueT, typename GenT, + typename std::enable_if<std::is_same<ValueT, double>::value, + int>::type = (GenT{}, 0)> +constexpr FlagDefaultArg DefaultArg(int) { + return {FlagDefaultSrc(GenT{}.value), FlagDefaultKind::kDouble}; +} + +template <typename ValueT, typename GenT> +constexpr FlagDefaultArg DefaultArg(char) { + return {FlagDefaultSrc(&GenT::Gen), FlagDefaultKind::kGenFunc}; +} /////////////////////////////////////////////////////////////////////////////// // Flag current value auxiliary structs. @@ -356,19 +417,19 @@ public: constexpr FlagImpl(const char* name, const char* filename, FlagOpFn op, FlagHelpArg help, FlagValueStorageKind value_kind, - FlagDfltGenFunc default_value_gen) + FlagDefaultArg default_arg) : name_(name), filename_(filename), op_(op), help_(help.source), help_source_kind_(static_cast<uint8_t>(help.kind)), value_storage_kind_(static_cast<uint8_t>(value_kind)), - def_kind_(static_cast<uint8_t>(FlagDefaultKind::kGenFunc)), + def_kind_(static_cast<uint8_t>(default_arg.kind)), modified_(false), on_command_line_(false), counter_(0), callback_(nullptr), - default_value_(default_value_gen), + default_value_(default_arg.source), data_guard_{} {} // Constant access methods @@ -444,10 +505,8 @@ // CommandLineFlag interface implementation absl::string_view Name() const override; std::string Filename() const override; - absl::string_view Typename() const override; std::string Help() const override; FlagFastTypeId TypeId() const override; - bool IsModified() const override ABSL_LOCKS_EXCLUDED(*DataGuard()); bool IsSpecifiedOnCommandLine() const override ABSL_LOCKS_EXCLUDED(*DataGuard()); std::string DefaultValue() const override ABSL_LOCKS_EXCLUDED(*DataGuard()); @@ -492,9 +551,10 @@ // Mutable flag's state (guarded by `data_guard_`). - // If def_kind_ == kDynamicValue, default_value_ holds a dynamically allocated - // value. - uint8_t def_kind_ : 1 ABSL_GUARDED_BY(*DataGuard()); + // def_kind_ is not guard by DataGuard() since it is accessed in Init without + // locks. If necessary we can decrease number of bits used to 2 by folding + // one_word storage cases. + uint8_t def_kind_ : 3; // Has this flag's value been modified? bool modified_ : 1 ABSL_GUARDED_BY(*DataGuard()); // Has this flag been specified on command line. @@ -530,10 +590,10 @@ template <typename T> class Flag { public: - constexpr Flag(const char* name, const char* filename, const FlagHelpArg help, - const FlagDfltGenFunc default_value_gen) + constexpr Flag(const char* name, const char* filename, FlagHelpArg help, + const FlagDefaultArg default_arg) : impl_(name, filename, &FlagOps<T>, help, - flags_internal::StorageKind<T>(), default_value_gen), + flags_internal::StorageKind<T>(), default_arg), value_() {} T Get() const { @@ -560,9 +620,7 @@ // CommandLineFlag interface absl::string_view Name() const { return impl_.Name(); } std::string Filename() const { return impl_.Filename(); } - absl::string_view Typename() const { return ""; } std::string Help() const { return impl_.Help(); } - bool IsModified() const { return impl_.IsModified(); } bool IsSpecifiedOnCommandLine() const { return impl_.IsSpecifiedOnCommandLine(); } @@ -662,20 +720,6 @@ Flag<T>* flag_; // Flag being registered (not owned). }; -// This struct and corresponding overload to MakeDefaultValue are used to -// facilitate usage of {} as default value in ABSL_FLAG macro. -struct EmptyBraces {}; - -template <typename T> -void MakeFromDefaultValue(void* dst, T t) { - new (dst) T(std::move(t)); -} - -template <typename T> -void MakeFromDefaultValue(void* dst, EmptyBraces) { - new (dst) T{}; -} - } // namespace flags_internal ABSL_NAMESPACE_END } // namespace absl
diff --git a/absl/flags/internal/private_handle_accessor.cc b/absl/flags/internal/private_handle_accessor.cc new file mode 100644 index 0000000..ec5776b --- /dev/null +++ b/absl/flags/internal/private_handle_accessor.cc
@@ -0,0 +1,52 @@ +// +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/flags/internal/private_handle_accessor.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace flags_internal { + +FlagFastTypeId PrivateHandleAccessor::TypeId(const CommandLineFlag& flag) { + return flag.TypeId(); +} + +std::unique_ptr<FlagStateInterface> PrivateHandleAccessor::SaveState( + CommandLineFlag* flag) { + return flag->SaveState(); +} + +bool PrivateHandleAccessor::ValidateInputValue(const CommandLineFlag& flag, + absl::string_view value) { + return flag.ValidateInputValue(value); +} + +void PrivateHandleAccessor::CheckDefaultValueParsingRoundtrip( + const CommandLineFlag& flag) { + flag.CheckDefaultValueParsingRoundtrip(); +} + +bool PrivateHandleAccessor::ParseFrom(CommandLineFlag* flag, + absl::string_view value, + flags_internal::FlagSettingMode set_mode, + flags_internal::ValueSource source, + std::string* error) { + return flag->ParseFrom(value, set_mode, source, error); +} + +} // namespace flags_internal +ABSL_NAMESPACE_END +} // namespace absl +
diff --git a/absl/flags/internal/private_handle_accessor.h b/absl/flags/internal/private_handle_accessor.h new file mode 100644 index 0000000..fbb4409 --- /dev/null +++ b/absl/flags/internal/private_handle_accessor.h
@@ -0,0 +1,52 @@ +// +// Copyright 2020 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_FLAGS_INTERNAL_PRIVATE_HANDLE_ACCESSOR_H_ +#define ABSL_FLAGS_INTERNAL_PRIVATE_HANDLE_ACCESSOR_H_ + +#include "absl/flags/internal/commandlineflag.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace flags_internal { + +// This class serves as a trampoline to access private methods of +// CommandLineFlag. This class is intended for use exclusively internally inside +// of the Abseil Flags implementation. +class PrivateHandleAccessor { + public: + // Access to CommandLineFlag::TypeId. + static FlagFastTypeId TypeId(const CommandLineFlag& flag); + + // Access to CommandLineFlag::SaveState. + static std::unique_ptr<FlagStateInterface> SaveState(CommandLineFlag* flag); + + // Access to CommandLineFlag::ValidateInputValue. + static bool ValidateInputValue(const CommandLineFlag& flag, + absl::string_view value); + + // Access to CommandLineFlag::CheckDefaultValueParsingRoundtrip. + static void CheckDefaultValueParsingRoundtrip(const CommandLineFlag& flag); + + static bool ParseFrom(CommandLineFlag* flag, absl::string_view value, + flags_internal::FlagSettingMode set_mode, + flags_internal::ValueSource source, std::string* error); +}; + +} // namespace flags_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_FLAGS_INTERNAL_PRIVATE_HANDLE_ACCESSOR_H_
diff --git a/absl/flags/internal/registry.cc b/absl/flags/internal/registry.cc index eb619c7..3b941f0 100644 --- a/absl/flags/internal/registry.cc +++ b/absl/flags/internal/registry.cc
@@ -29,6 +29,7 @@ #include "absl/base/internal/raw_logging.h" #include "absl/base/thread_annotations.h" #include "absl/flags/internal/commandlineflag.h" +#include "absl/flags/internal/private_handle_accessor.h" #include "absl/flags/usage_config.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" @@ -127,14 +128,13 @@ (flag->IsRetired() ? old_flag->Filename() : flag->Filename()), "'."), true); - } else if (flag->TypeId() != old_flag->TypeId()) { + } else if (flags_internal::PrivateHandleAccessor::TypeId(*flag) != + flags_internal::PrivateHandleAccessor::TypeId(*old_flag)) { flags_internal::ReportUsageError( absl::StrCat("Flag '", flag->Name(), "' was defined more than once but with " "differing types. Defined in files '", - old_flag->Filename(), "' and '", flag->Filename(), - "' with types '", old_flag->Typename(), "' and '", - flag->Typename(), "', respectively."), + old_flag->Filename(), "' and '", flag->Filename(), "'."), true); } else if (old_flag->IsRetired()) { // Retired flag can just be deleted. @@ -206,7 +206,8 @@ void SaveFromRegistry() { assert(backup_registry_.empty()); // call only once! flags_internal::ForEachFlag([&](flags_internal::CommandLineFlag* flag) { - if (auto flag_state = flag->SaveState()) { + if (auto flag_state = + flags_internal::PrivateHandleAccessor::SaveState(flag)) { backup_registry_.emplace_back(std::move(flag_state)); } }); @@ -290,11 +291,9 @@ private: absl::string_view Name() const override { return name_; } std::string Filename() const override { return "RETIRED"; } - absl::string_view Typename() const override { return ""; } FlagFastTypeId TypeId() const override { return type_id_; } std::string Help() const override { return ""; } bool IsRetired() const override { return true; } - bool IsModified() const override { return false; } bool IsSpecifiedOnCommandLine() const override { return false; } std::string DefaultValue() const override { return ""; } std::string CurrentValue() const override { return ""; }
diff --git a/absl/flags/internal/type_erased.cc b/absl/flags/internal/type_erased.cc index 75b4cdf..3cfc9b2 100644 --- a/absl/flags/internal/type_erased.cc +++ b/absl/flags/internal/type_erased.cc
@@ -22,6 +22,7 @@ #include "absl/base/config.h" #include "absl/base/internal/raw_logging.h" #include "absl/flags/internal/commandlineflag.h" +#include "absl/flags/internal/private_handle_accessor.h" #include "absl/flags/internal/registry.h" #include "absl/flags/usage_config.h" #include "absl/strings/string_view.h" @@ -56,7 +57,8 @@ if (!flag || flag->IsRetired()) return false; std::string error; - if (!flag->ParseFrom(value, set_mode, kProgrammaticChange, &error)) { + if (!flags_internal::PrivateHandleAccessor::ParseFrom( + flag, value, set_mode, kProgrammaticChange, &error)) { // Errors here are all of the form: the provided name was a recognized // flag, but the value was invalid (bad type, or validation failed). flags_internal::ReportUsageError(error, false); @@ -72,7 +74,9 @@ CommandLineFlag* flag = flags_internal::FindCommandLineFlag(name); return flag != nullptr && - (flag->IsRetired() || flag->ValidateInputValue(value)); + (flag->IsRetired() || + flags_internal::PrivateHandleAccessor::ValidateInputValue(*flag, + value)); } // --------------------------------------------------------------------
diff --git a/absl/flags/internal/type_erased.h b/absl/flags/internal/type_erased.h index 188429c..ffe319b 100644 --- a/absl/flags/internal/type_erased.h +++ b/absl/flags/internal/type_erased.h
@@ -75,7 +75,7 @@ CommandLineFlag* flag = flags_internal::FindCommandLineFlag(name); if (!flag) return false; - if (auto val = flag->Get<T>()) { + if (auto val = flag->TryGet<T>()) { *dst = *val; return true; }
diff --git a/absl/flags/internal/usage.cc b/absl/flags/internal/usage.cc index a9a5cba..10accc4 100644 --- a/absl/flags/internal/usage.cc +++ b/absl/flags/internal/usage.cc
@@ -27,6 +27,7 @@ #include "absl/flags/internal/commandlineflag.h" #include "absl/flags/internal/flag.h" #include "absl/flags/internal/path_util.h" +#include "absl/flags/internal/private_handle_accessor.h" #include "absl/flags/internal/program_name.h" #include "absl/flags/internal/registry.h" #include "absl/flags/usage_config.h" @@ -54,27 +55,6 @@ namespace flags_internal { namespace { -absl::string_view TypenameForHelp(const flags_internal::CommandLineFlag& flag) { - // Only report names of v1 built-in types -#define HANDLE_V1_BUILTIN_TYPE(t) \ - if (flag.IsOfType<t>()) { \ - return #t; \ - } - - HANDLE_V1_BUILTIN_TYPE(bool); - HANDLE_V1_BUILTIN_TYPE(int32_t); - HANDLE_V1_BUILTIN_TYPE(int64_t); - HANDLE_V1_BUILTIN_TYPE(uint64_t); - HANDLE_V1_BUILTIN_TYPE(double); -#undef HANDLE_V1_BUILTIN_TYPE - - if (flag.IsOfType<std::string>()) { - return "string"; - } - - return ""; -} - // This class is used to emit an XML element with `tag` and `text`. // It adds opening and closing tags and escapes special characters in the text. // For example: @@ -212,23 +192,20 @@ // Flag help. printer.Write(absl::StrCat("(", flag.Help(), ");"), /*wrap_line=*/true); - // Flag data type (for V1 flags only). - if (!flag.IsAbseilFlag() && !flag.IsRetired()) { - printer.Write(absl::StrCat("type: ", TypenameForHelp(flag), ";")); - } - // The listed default value will be the actual default from the flag // definition in the originating source file, unless the value has // subsequently been modified using SetCommandLineOption() with mode // SET_FLAGS_DEFAULT. std::string dflt_val = flag.DefaultValue(); + std::string curr_val = flag.CurrentValue(); + bool is_modified = curr_val != dflt_val; + if (flag.IsOfType<std::string>()) { dflt_val = absl::StrCat("\"", dflt_val, "\""); } printer.Write(absl::StrCat("default: ", dflt_val, ";")); - if (flag.IsModified()) { - std::string curr_val = flag.CurrentValue(); + if (is_modified) { if (flag.IsOfType<std::string>()) { curr_val = absl::StrCat("\"", curr_val, "\""); }
diff --git a/absl/flags/parse.cc b/absl/flags/parse.cc index b60b36f..66a28a9 100644 --- a/absl/flags/parse.cc +++ b/absl/flags/parse.cc
@@ -39,6 +39,7 @@ #include "absl/flags/internal/commandlineflag.h" #include "absl/flags/internal/flag.h" #include "absl/flags/internal/parse.h" +#include "absl/flags/internal/private_handle_accessor.h" #include "absl/flags/internal/program_name.h" #include "absl/flags/internal/registry.h" #include "absl/flags/internal/usage.h" @@ -298,7 +299,8 @@ ABSL_FLAGS_INTERNAL_BUILTIN_TYPES(IGNORE_TYPE) #undef IGNORE_TYPE - flag->CheckDefaultValueParsingRoundtrip(); + flags_internal::PrivateHandleAccessor::CheckDefaultValueParsingRoundtrip( + *flag); }); #endif } @@ -696,7 +698,8 @@ if (flag->IsRetired()) continue; std::string error; - if (!flag->ParseFrom(value, SET_FLAGS_VALUE, kCommandLine, &error)) { + if (!flags_internal::PrivateHandleAccessor::ParseFrom( + flag, value, SET_FLAGS_VALUE, kCommandLine, &error)) { flags_internal::ReportUsageError(error, true); success = false; }
diff --git a/absl/hash/hash.h b/absl/hash/hash.h index 3dbeab6..d7386f6 100644 --- a/absl/hash/hash.h +++ b/absl/hash/hash.h
@@ -37,8 +37,11 @@ // types. Hashing of that combined state is separately done by `absl::Hash`. // // One should assume that a hash algorithm is chosen randomly at the start of -// each process. E.g., absl::Hash<int>()(9) in one process and -// absl::Hash<int>()(9) in another process are likely to differ. +// each process. E.g., `absl::Hash<int>{}(9)` in one process and +// `absl::Hash<int>{}(9)` in another process are likely to differ. +// +// `absl::Hash` is intended to strongly mix input bits with a target of passing +// an [Avalanche Test](https://en.wikipedia.org/wiki/Avalanche_effect). // // Example: //
diff --git a/absl/random/gaussian_distribution_test.cc b/absl/random/gaussian_distribution_test.cc index 49c0751..398f013 100644 --- a/absl/random/gaussian_distribution_test.cc +++ b/absl/random/gaussian_distribution_test.cc
@@ -130,12 +130,15 @@ ss >> after; #if defined(__powerpc64__) || defined(__PPC64__) || defined(__powerpc__) || \ - defined(__ppc__) || defined(__PPC__) + defined(__ppc__) || defined(__PPC__) || defined(__EMSCRIPTEN__) if (std::is_same<TypeParam, long double>::value) { // Roundtripping floating point values requires sufficient precision // to reconstruct the exact value. It turns out that long double // has some errors doing this on ppc, particularly for values // near {1.0 +/- epsilon}. + // + // Emscripten is even worse, implementing long double as a 128-bit + // type, but shipping with a strtold() that doesn't support that. if (mean <= std::numeric_limits<double>::max() && mean >= std::numeric_limits<double>::lowest()) { EXPECT_EQ(static_cast<double>(before.mean()),
diff --git a/absl/random/internal/wide_multiply_test.cc b/absl/random/internal/wide_multiply_test.cc index 922603f..ca8ce92 100644 --- a/absl/random/internal/wide_multiply_test.cc +++ b/absl/random/internal/wide_multiply_test.cc
@@ -28,7 +28,7 @@ EXPECT_EQ(absl::uint128(0), MultiplyU64ToU128(0, 0)); - // Max uint64 + // Max uint64_t EXPECT_EQ(MultiplyU64ToU128(kMax, kMax), absl::MakeUint128(0xfffffffffffffffe, 0x0000000000000001)); EXPECT_EQ(absl::MakeUint128(0, kMax), MultiplyU64ToU128(kMax, 1));
diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel index 8aecbe5..8220896 100644 --- a/absl/strings/BUILD.bazel +++ b/absl/strings/BUILD.bazel
@@ -638,10 +638,13 @@ visibility = ["//visibility:private"], deps = [ ":strings", + "//absl/base:bits", "//absl/base:config", "//absl/base:core_headers", + "//absl/functional:function_ref", "//absl/meta:type_traits", "//absl/numeric:int128", + "//absl/types:optional", "//absl/types:span", ], ) @@ -718,7 +721,7 @@ deps = [ ":str_format_internal", "//absl/base:raw_logging_internal", - "//absl/numeric:int128", + "//absl/types:optional", "@com_google_googletest//:gtest_main", ], )
diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt index 003794f..c0ea0c8 100644 --- a/absl/strings/CMakeLists.txt +++ b/absl/strings/CMakeLists.txt
@@ -392,6 +392,7 @@ COPTS ${ABSL_DEFAULT_COPTS} DEPS + absl::bits absl::strings absl::config absl::core_headers
diff --git a/absl/strings/cord.cc b/absl/strings/cord.cc index fa490c1..0403cc6 100644 --- a/absl/strings/cord.cc +++ b/absl/strings/cord.cc
@@ -15,6 +15,7 @@ #include "absl/strings/cord.h" #include <algorithm> +#include <atomic> #include <cstddef> #include <cstdio> #include <cstdlib>
diff --git a/absl/strings/cord.h b/absl/strings/cord.h index ae3d2e7..86ae76f 100644 --- a/absl/strings/cord.h +++ b/absl/strings/cord.h
@@ -162,7 +162,7 @@ if (contents_.is_tree()) DestroyCordSlow(); } - // Cord::MakeCordFromExternal(data, callable) + // MakeCordFromExternal() // // Creates a Cord that takes ownership of external string memory. The // contents of `data` are not copied to the Cord; instead, the external @@ -246,10 +246,17 @@ // (pos + new_size) >= size(), the result is the subrange [pos, size()). Cord Subcord(size_t pos, size_t new_size) const; + // Cord::swap() + // + // Swaps the contents of the Cord with `other`. + void swap(Cord& other) noexcept; + // swap() // - // Swaps the data of Cord `x` with Cord `y`. - friend void swap(Cord& x, Cord& y) noexcept; + // Swaps the contents of two Cords. + friend void swap(Cord& x, Cord& y) noexcept { + x.swap(y); + } // Cord::size() // @@ -1032,6 +1039,10 @@ inline Cord::Cord(Cord&& src) noexcept : contents_(std::move(src.contents_)) {} +inline void Cord::swap(Cord& other) noexcept { + contents_.Swap(&other.contents_); +} + inline Cord& Cord::operator=(Cord&& x) noexcept { contents_ = std::move(x.contents_); return *this; @@ -1308,10 +1319,6 @@ inline bool operator>=(const Cord& x, absl::string_view y) { return !(x < y); } inline bool operator>=(absl::string_view x, const Cord& y) { return !(x < y); } -// Overload of swap for Cord. The use of non-const references is -// required. :( -inline void swap(Cord& x, Cord& y) noexcept { y.contents_.Swap(&x.contents_); } - // Some internals exposed to test code. namespace strings_internal { class CordTestAccess {
diff --git a/absl/strings/cord_test.cc b/absl/strings/cord_test.cc index 336cedd..4443c82 100644 --- a/absl/strings/cord_test.cc +++ b/absl/strings/cord_test.cc
@@ -396,6 +396,9 @@ swap(x, y); ASSERT_EQ(x, absl::Cord(b)); ASSERT_EQ(y, absl::Cord(a)); + x.swap(y); + ASSERT_EQ(x, absl::Cord(a)); + ASSERT_EQ(y, absl::Cord(b)); } static void VerifyCopyToString(const absl::Cord& cord) {
diff --git a/absl/strings/internal/str_format/arg.cc b/absl/strings/internal/str_format/arg.cc index 1284227..964f25f 100644 --- a/absl/strings/internal/str_format/arg.cc +++ b/absl/strings/internal/str_format/arg.cc
@@ -167,24 +167,26 @@ // Note: 'o' conversions do not have a base indicator, it's just that // the '#' flag is specified to modify the precision for 'o' conversions. string_view BaseIndicator(const IntDigits &as_digits, - const ConversionSpec conv) { + const FormatConversionSpecImpl conv) { // always show 0x for %p. - bool alt = conv.has_alt_flag() || conv.conversion_char() == ConversionChar::p; - bool hex = (conv.conversion_char() == FormatConversionChar::x || - conv.conversion_char() == FormatConversionChar::X || - conv.conversion_char() == FormatConversionChar::p); + bool alt = conv.has_alt_flag() || + conv.conversion_char() == FormatConversionCharInternal::p; + bool hex = (conv.conversion_char() == FormatConversionCharInternal::x || + conv.conversion_char() == FormatConversionCharInternal::X || + conv.conversion_char() == FormatConversionCharInternal::p); // From the POSIX description of '#' flag: // "For x or X conversion specifiers, a non-zero result shall have // 0x (or 0X) prefixed to it." if (alt && hex && !as_digits.without_neg_or_zero().empty()) { - return conv.conversion_char() == FormatConversionChar::X ? "0X" : "0x"; + return conv.conversion_char() == FormatConversionCharInternal::X ? "0X" + : "0x"; } return {}; } -string_view SignColumn(bool neg, const ConversionSpec conv) { - if (conv.conversion_char() == FormatConversionChar::d || - conv.conversion_char() == FormatConversionChar::i) { +string_view SignColumn(bool neg, const FormatConversionSpecImpl conv) { + if (conv.conversion_char() == FormatConversionCharInternal::d || + conv.conversion_char() == FormatConversionCharInternal::i) { if (neg) return "-"; if (conv.has_show_pos_flag()) return "+"; if (conv.has_sign_col_flag()) return " "; @@ -192,7 +194,7 @@ return {}; } -bool ConvertCharImpl(unsigned char v, const ConversionSpec conv, +bool ConvertCharImpl(unsigned char v, const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { size_t fill = 0; if (conv.width() >= 0) fill = conv.width(); @@ -204,7 +206,8 @@ } bool ConvertIntImplInnerSlow(const IntDigits &as_digits, - const ConversionSpec conv, FormatSinkImpl *sink) { + const FormatConversionSpecImpl conv, + FormatSinkImpl *sink) { // Print as a sequence of Substrings: // [left_spaces][sign][base_indicator][zeroes][formatted][right_spaces] size_t fill = 0; @@ -224,7 +227,8 @@ if (!precision_specified) precision = 1; - if (conv.has_alt_flag() && conv.conversion_char() == ConversionChar::o) { + if (conv.has_alt_flag() && + conv.conversion_char() == FormatConversionCharInternal::o) { // From POSIX description of the '#' (alt) flag: // "For o conversion, it increases the precision (if necessary) to // force the first digit of the result to be zero." @@ -258,42 +262,43 @@ } template <typename T> -bool ConvertIntArg(T v, const ConversionSpec conv, FormatSinkImpl *sink) { +bool ConvertIntArg(T v, const FormatConversionSpecImpl conv, + FormatSinkImpl *sink) { using U = typename MakeUnsigned<T>::type; IntDigits as_digits; switch (conv.conversion_char()) { - case FormatConversionChar::c: + case FormatConversionCharInternal::c: return ConvertCharImpl(static_cast<unsigned char>(v), conv, sink); - case FormatConversionChar::o: + case FormatConversionCharInternal::o: as_digits.PrintAsOct(static_cast<U>(v)); break; - case FormatConversionChar::x: + case FormatConversionCharInternal::x: as_digits.PrintAsHexLower(static_cast<U>(v)); break; - case FormatConversionChar::X: + case FormatConversionCharInternal::X: as_digits.PrintAsHexUpper(static_cast<U>(v)); break; - case FormatConversionChar::u: + case FormatConversionCharInternal::u: as_digits.PrintAsDec(static_cast<U>(v)); break; - case FormatConversionChar::d: - case FormatConversionChar::i: + case FormatConversionCharInternal::d: + case FormatConversionCharInternal::i: as_digits.PrintAsDec(v); break; - case FormatConversionChar::a: - case FormatConversionChar::e: - case FormatConversionChar::f: - case FormatConversionChar::g: - case FormatConversionChar::A: - case FormatConversionChar::E: - case FormatConversionChar::F: - case FormatConversionChar::G: + case FormatConversionCharInternal::a: + case FormatConversionCharInternal::e: + case FormatConversionCharInternal::f: + case FormatConversionCharInternal::g: + case FormatConversionCharInternal::A: + case FormatConversionCharInternal::E: + case FormatConversionCharInternal::F: + case FormatConversionCharInternal::G: return ConvertFloatImpl(static_cast<double>(v), conv, sink); default: @@ -308,14 +313,15 @@ } template <typename T> -bool ConvertFloatArg(T v, const ConversionSpec conv, FormatSinkImpl *sink) { +bool ConvertFloatArg(T v, const FormatConversionSpecImpl conv, + FormatSinkImpl *sink) { return FormatConversionCharIsFloat(conv.conversion_char()) && ConvertFloatImpl(v, conv, sink); } -inline bool ConvertStringArg(string_view v, const ConversionSpec conv, +inline bool ConvertStringArg(string_view v, const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { - if (conv.conversion_char() != ConversionChar::s) return false; + if (conv.conversion_char() != FormatConversionCharInternal::s) return false; if (conv.is_basic()) { sink->Append(v); return true; @@ -327,22 +333,23 @@ } // namespace // ==================== Strings ==================== -ConvertResult<Conv::s> FormatConvertImpl(const std::string &v, - const ConversionSpec conv, - FormatSinkImpl *sink) { +StringConvertResult FormatConvertImpl(const std::string &v, + const FormatConversionSpecImpl conv, + FormatSinkImpl *sink) { return {ConvertStringArg(v, conv, sink)}; } -ConvertResult<Conv::s> FormatConvertImpl(string_view v, - const ConversionSpec conv, - FormatSinkImpl *sink) { +StringConvertResult FormatConvertImpl(string_view v, + const FormatConversionSpecImpl conv, + FormatSinkImpl *sink) { return {ConvertStringArg(v, conv, sink)}; } -ConvertResult<Conv::s | Conv::p> FormatConvertImpl(const char *v, - const ConversionSpec conv, - FormatSinkImpl *sink) { - if (conv.conversion_char() == ConversionChar::p) +ArgConvertResult<FormatConversionCharSetUnion( + FormatConversionCharSetInternal::s, FormatConversionCharSetInternal::p)> +FormatConvertImpl(const char *v, const FormatConversionSpecImpl conv, + FormatSinkImpl *sink) { + if (conv.conversion_char() == FormatConversionCharInternal::p) return {FormatConvertImpl(VoidPtr(v), conv, sink).value}; size_t len; if (v == nullptr) { @@ -357,9 +364,9 @@ } // ==================== Raw pointers ==================== -ConvertResult<Conv::p> FormatConvertImpl(VoidPtr v, const ConversionSpec conv, - FormatSinkImpl *sink) { - if (conv.conversion_char() != ConversionChar::p) return {false}; +ArgConvertResult<FormatConversionCharSetInternal::p> FormatConvertImpl( + VoidPtr v, const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { + if (conv.conversion_char() != FormatConversionCharInternal::p) return {false}; if (!v.value) { sink->Append("(nil)"); return {true}; @@ -370,82 +377,87 @@ } // ==================== Floats ==================== -FloatingConvertResult FormatConvertImpl(float v, const ConversionSpec conv, +FloatingConvertResult FormatConvertImpl(float v, + const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { return {ConvertFloatArg(v, conv, sink)}; } -FloatingConvertResult FormatConvertImpl(double v, const ConversionSpec conv, +FloatingConvertResult FormatConvertImpl(double v, + const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { return {ConvertFloatArg(v, conv, sink)}; } FloatingConvertResult FormatConvertImpl(long double v, - const ConversionSpec conv, + const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { return {ConvertFloatArg(v, conv, sink)}; } // ==================== Chars ==================== -IntegralConvertResult FormatConvertImpl(char v, const ConversionSpec conv, +IntegralConvertResult FormatConvertImpl(char v, + const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } IntegralConvertResult FormatConvertImpl(signed char v, - const ConversionSpec conv, + const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } IntegralConvertResult FormatConvertImpl(unsigned char v, - const ConversionSpec conv, + const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } // ==================== Ints ==================== IntegralConvertResult FormatConvertImpl(short v, // NOLINT - const ConversionSpec conv, + const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } IntegralConvertResult FormatConvertImpl(unsigned short v, // NOLINT - const ConversionSpec conv, + const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } -IntegralConvertResult FormatConvertImpl(int v, const ConversionSpec conv, +IntegralConvertResult FormatConvertImpl(int v, + const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } -IntegralConvertResult FormatConvertImpl(unsigned v, const ConversionSpec conv, +IntegralConvertResult FormatConvertImpl(unsigned v, + const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } IntegralConvertResult FormatConvertImpl(long v, // NOLINT - const ConversionSpec conv, + const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } IntegralConvertResult FormatConvertImpl(unsigned long v, // NOLINT - const ConversionSpec conv, + const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } IntegralConvertResult FormatConvertImpl(long long v, // NOLINT - const ConversionSpec conv, + const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } IntegralConvertResult FormatConvertImpl(unsigned long long v, // NOLINT - const ConversionSpec conv, + const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } IntegralConvertResult FormatConvertImpl(absl::int128 v, - const ConversionSpec conv, + const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } IntegralConvertResult FormatConvertImpl(absl::uint128 v, - const ConversionSpec conv, + const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; }
diff --git a/absl/strings/internal/str_format/arg.h b/absl/strings/internal/str_format/arg.h index 1c36e30..9a1e5ef 100644 --- a/absl/strings/internal/str_format/arg.h +++ b/absl/strings/internal/str_format/arg.h
@@ -31,10 +31,11 @@ struct HasUserDefinedConvert : std::false_type {}; template <typename T> -struct HasUserDefinedConvert< - T, void_t<decltype(AbslFormatConvert( - std::declval<const T&>(), std::declval<ConversionSpec>(), - std::declval<FormatSink*>()))>> : std::true_type {}; +struct HasUserDefinedConvert<T, void_t<decltype(AbslFormatConvert( + std::declval<const T&>(), + std::declval<const FormatConversionSpec&>(), + std::declval<FormatSink*>()))>> + : std::true_type {}; template <typename T> class StreamedWrapper; @@ -52,25 +53,40 @@ : value(ptr ? reinterpret_cast<uintptr_t>(ptr) : 0) {} uintptr_t value; }; -ConvertResult<Conv::p> FormatConvertImpl(VoidPtr v, ConversionSpec conv, - FormatSinkImpl* sink); + +template <FormatConversionCharSet C> +struct ArgConvertResult { + bool value; +}; + +template <FormatConversionCharSet C> +constexpr FormatConversionCharSet ExtractCharSet(ArgConvertResult<C>) { + return C; +} + +using StringConvertResult = + ArgConvertResult<FormatConversionCharSetInternal::s>; +ArgConvertResult<FormatConversionCharSetInternal::p> FormatConvertImpl( + VoidPtr v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); // Strings. -ConvertResult<Conv::s> FormatConvertImpl(const std::string& v, - ConversionSpec conv, - FormatSinkImpl* sink); -ConvertResult<Conv::s> FormatConvertImpl(string_view v, ConversionSpec conv, - FormatSinkImpl* sink); -ConvertResult<Conv::s | Conv::p> FormatConvertImpl(const char* v, - ConversionSpec conv, - FormatSinkImpl* sink); -template <class AbslCord, - typename std::enable_if< - std::is_same<AbslCord, absl::Cord>::value>::type* = nullptr> -ConvertResult<Conv::s> FormatConvertImpl(const AbslCord& value, - ConversionSpec conv, - FormatSinkImpl* sink) { - if (conv.conversion_char() != ConversionChar::s) { +StringConvertResult FormatConvertImpl(const std::string& v, + FormatConversionSpecImpl conv, + FormatSinkImpl* sink); +StringConvertResult FormatConvertImpl(string_view v, + FormatConversionSpecImpl conv, + FormatSinkImpl* sink); +ArgConvertResult<FormatConversionCharSetUnion( + FormatConversionCharSetInternal::s, FormatConversionCharSetInternal::p)> +FormatConvertImpl(const char* v, const FormatConversionSpecImpl conv, + FormatSinkImpl* sink); + +template <class AbslCord, typename std::enable_if<std::is_same< + AbslCord, absl::Cord>::value>::type* = nullptr> +StringConvertResult FormatConvertImpl(const AbslCord& value, + FormatConversionSpecImpl conv, + FormatSinkImpl* sink) { + if (conv.conversion_char() != FormatConversionCharInternal::s) { return {false}; } @@ -107,55 +123,63 @@ return {true}; } -using IntegralConvertResult = - ConvertResult<Conv::c | Conv::kNumeric | Conv::kStar>; -using FloatingConvertResult = ConvertResult<Conv::kFloating>; +using IntegralConvertResult = ArgConvertResult<FormatConversionCharSetUnion( + FormatConversionCharSetInternal::c, + FormatConversionCharSetInternal::kNumeric, + FormatConversionCharSetInternal::kStar)>; +using FloatingConvertResult = + ArgConvertResult<FormatConversionCharSetInternal::kFloating>; // Floats. -FloatingConvertResult FormatConvertImpl(float v, ConversionSpec conv, +FloatingConvertResult FormatConvertImpl(float v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); -FloatingConvertResult FormatConvertImpl(double v, ConversionSpec conv, +FloatingConvertResult FormatConvertImpl(double v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); -FloatingConvertResult FormatConvertImpl(long double v, ConversionSpec conv, +FloatingConvertResult FormatConvertImpl(long double v, + FormatConversionSpecImpl conv, FormatSinkImpl* sink); // Chars. -IntegralConvertResult FormatConvertImpl(char v, ConversionSpec conv, +IntegralConvertResult FormatConvertImpl(char v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); -IntegralConvertResult FormatConvertImpl(signed char v, ConversionSpec conv, +IntegralConvertResult FormatConvertImpl(signed char v, + FormatConversionSpecImpl conv, FormatSinkImpl* sink); -IntegralConvertResult FormatConvertImpl(unsigned char v, ConversionSpec conv, +IntegralConvertResult FormatConvertImpl(unsigned char v, + FormatConversionSpecImpl conv, FormatSinkImpl* sink); // Ints. IntegralConvertResult FormatConvertImpl(short v, // NOLINT - ConversionSpec conv, + FormatConversionSpecImpl conv, FormatSinkImpl* sink); IntegralConvertResult FormatConvertImpl(unsigned short v, // NOLINT - ConversionSpec conv, + FormatConversionSpecImpl conv, FormatSinkImpl* sink); -IntegralConvertResult FormatConvertImpl(int v, ConversionSpec conv, +IntegralConvertResult FormatConvertImpl(int v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); -IntegralConvertResult FormatConvertImpl(unsigned v, ConversionSpec conv, +IntegralConvertResult FormatConvertImpl(unsigned v, + FormatConversionSpecImpl conv, FormatSinkImpl* sink); IntegralConvertResult FormatConvertImpl(long v, // NOLINT - ConversionSpec conv, + FormatConversionSpecImpl conv, FormatSinkImpl* sink); IntegralConvertResult FormatConvertImpl(unsigned long v, // NOLINT - ConversionSpec conv, + FormatConversionSpecImpl conv, FormatSinkImpl* sink); IntegralConvertResult FormatConvertImpl(long long v, // NOLINT - ConversionSpec conv, + FormatConversionSpecImpl conv, FormatSinkImpl* sink); IntegralConvertResult FormatConvertImpl(unsigned long long v, // NOLINT - ConversionSpec conv, + FormatConversionSpecImpl conv, FormatSinkImpl* sink); -IntegralConvertResult FormatConvertImpl(int128 v, ConversionSpec conv, +IntegralConvertResult FormatConvertImpl(int128 v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); -IntegralConvertResult FormatConvertImpl(uint128 v, ConversionSpec conv, +IntegralConvertResult FormatConvertImpl(uint128 v, + FormatConversionSpecImpl conv, FormatSinkImpl* sink); template <typename T, enable_if_t<std::is_same<T, bool>::value, int> = 0> -IntegralConvertResult FormatConvertImpl(T v, ConversionSpec conv, +IntegralConvertResult FormatConvertImpl(T v, FormatConversionSpecImpl conv, FormatSinkImpl* sink) { return FormatConvertImpl(static_cast<int>(v), conv, sink); } @@ -166,12 +190,12 @@ typename std::enable_if<std::is_enum<T>::value && !HasUserDefinedConvert<T>::value, IntegralConvertResult>::type -FormatConvertImpl(T v, ConversionSpec conv, FormatSinkImpl* sink); +FormatConvertImpl(T v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); template <typename T> -ConvertResult<Conv::s> FormatConvertImpl(const StreamedWrapper<T>& v, - ConversionSpec conv, - FormatSinkImpl* out) { +StringConvertResult FormatConvertImpl(const StreamedWrapper<T>& v, + FormatConversionSpecImpl conv, + FormatSinkImpl* out) { std::ostringstream oss; oss << v.v_; if (!oss) return {false}; @@ -182,12 +206,13 @@ // until after FormatCountCapture is fully defined. struct FormatCountCaptureHelper { template <class T = int> - static ConvertResult<Conv::n> ConvertHelper(const FormatCountCapture& v, - ConversionSpec conv, - FormatSinkImpl* sink) { + static ArgConvertResult<FormatConversionCharSetInternal::n> ConvertHelper( + const FormatCountCapture& v, FormatConversionSpecImpl conv, + FormatSinkImpl* sink) { const absl::enable_if_t<sizeof(T) != 0, FormatCountCapture>& v2 = v; - if (conv.conversion_char() != str_format_internal::ConversionChar::n) { + if (conv.conversion_char() != + str_format_internal::FormatConversionCharInternal::n) { return {false}; } *v2.p_ = static_cast<int>(sink->size()); @@ -196,9 +221,9 @@ }; template <class T = int> -ConvertResult<Conv::n> FormatConvertImpl(const FormatCountCapture& v, - ConversionSpec conv, - FormatSinkImpl* sink) { +ArgConvertResult<FormatConversionCharSetInternal::n> FormatConvertImpl( + const FormatCountCapture& v, FormatConversionSpecImpl conv, + FormatSinkImpl* sink) { return FormatCountCaptureHelper::ConvertHelper(v, conv, sink); } @@ -207,13 +232,13 @@ struct FormatArgImplFriend { template <typename Arg> static bool ToInt(Arg arg, int* out) { - // A value initialized ConversionSpec has a `none` conv, which tells the - // dispatcher to run the `int` conversion. + // A value initialized FormatConversionSpecImpl has a `none` conv, which + // tells the dispatcher to run the `int` conversion. return arg.dispatcher_(arg.data_, {}, out); } template <typename Arg> - static bool Convert(Arg arg, str_format_internal::ConversionSpec conv, + static bool Convert(Arg arg, FormatConversionSpecImpl conv, FormatSinkImpl* out) { return arg.dispatcher_(arg.data_, conv, out); } @@ -237,7 +262,7 @@ char buf[kInlinedSpace]; }; - using Dispatcher = bool (*)(Data, ConversionSpec, void* out); + using Dispatcher = bool (*)(Data, FormatConversionSpecImpl, void* out); template <typename T> struct store_by_value @@ -379,9 +404,10 @@ } template <typename T> - static bool Dispatch(Data arg, ConversionSpec spec, void* out) { + static bool Dispatch(Data arg, FormatConversionSpecImpl spec, void* out) { // A `none` conv indicates that we want the `int` conversion. - if (ABSL_PREDICT_FALSE(spec.conversion_char() == ConversionChar::kNone)) { + if (ABSL_PREDICT_FALSE(spec.conversion_char() == + FormatConversionCharInternal::kNone)) { return ToInt<T>(arg, static_cast<int*>(out), std::is_integral<T>(), std::is_enum<T>()); } @@ -395,8 +421,9 @@ Dispatcher dispatcher_; }; -#define ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(T, E) \ - E template bool FormatArgImpl::Dispatch<T>(Data, ConversionSpec, void*) +#define ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(T, E) \ + E template bool FormatArgImpl::Dispatch<T>(Data, FormatConversionSpecImpl, \ + void*) #define ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_(...) \ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(str_format_internal::VoidPtr, \
diff --git a/absl/strings/internal/str_format/arg_test.cc b/absl/strings/internal/str_format/arg_test.cc index 8d30d8b..37e5b75 100644 --- a/absl/strings/internal/str_format/arg_test.cc +++ b/absl/strings/internal/str_format/arg_test.cc
@@ -95,8 +95,9 @@ TEST_F(FormatArgImplTest, WorksWithCharArraysOfUnknownSize) { std::string s; FormatSinkImpl sink(&s); - ConversionSpec conv; - FormatConversionSpecImplFriend::SetConversionChar(ConversionChar::s, &conv); + FormatConversionSpecImpl conv; + FormatConversionSpecImplFriend::SetConversionChar(FormatConversionChar::s, + &conv); FormatConversionSpecImplFriend::SetFlags(Flags(), &conv); FormatConversionSpecImplFriend::SetWidth(-1, &conv); FormatConversionSpecImplFriend::SetPrecision(-1, &conv);
diff --git a/absl/strings/internal/str_format/bind.h b/absl/strings/internal/str_format/bind.h index d30fdf5..585246e 100644 --- a/absl/strings/internal/str_format/bind.h +++ b/absl/strings/internal/str_format/bind.h
@@ -19,7 +19,7 @@ namespace str_format_internal { -class BoundConversion : public ConversionSpec { +class BoundConversion : public FormatConversionSpecImpl { public: const FormatArgImpl* arg() const { return arg_; } void set_arg(const FormatArgImpl* a) { arg_ = a; } @@ -119,7 +119,7 @@ #endif // ABSL_INTERNAL_ENABLE_FORMAT_CHECKER - template <Conv... C, + template <FormatConversionCharSet... C, typename = typename std::enable_if< AllOf(sizeof...(C) == sizeof...(Args), Contains(Args, C)...)>::type> @@ -189,9 +189,9 @@ private: template <typename S> - friend ConvertResult<Conv::s> FormatConvertImpl(const StreamedWrapper<S>& v, - ConversionSpec conv, - FormatSinkImpl* out); + friend ArgConvertResult<FormatConversionCharSetInternal::s> FormatConvertImpl( + const StreamedWrapper<S>& v, FormatConversionSpecImpl conv, + FormatSinkImpl* out); const T& v_; };
diff --git a/absl/strings/internal/str_format/checker.h b/absl/strings/internal/str_format/checker.h index 8993a79..73ef05f 100644 --- a/absl/strings/internal/str_format/checker.h +++ b/absl/strings/internal/str_format/checker.h
@@ -25,10 +25,12 @@ } template <typename Arg> -constexpr Conv ArgumentToConv() { - return decltype(str_format_internal::FormatConvertImpl( - std::declval<const Arg&>(), std::declval<const ConversionSpec&>(), - std::declval<FormatSinkImpl*>()))::kConv; +constexpr FormatConversionCharSet ArgumentToConv() { + return absl::str_format_internal::ExtractCharSet( + decltype(str_format_internal::FormatConvertImpl( + std::declval<const Arg&>(), + std::declval<const FormatConversionSpecImpl&>(), + std::declval<FormatSinkImpl*>())){}); } #ifdef ABSL_INTERNAL_ENABLE_FORMAT_CHECKER @@ -39,14 +41,14 @@ // A constexpr compatible list of Convs. struct ConvList { - const Conv* array; + const FormatConversionCharSet* array; int count; // We do the bound check here to avoid having to do it on the callers. - // Returning an empty Conv has the same effect as short circuiting because it - // will never match any conversion. - constexpr Conv operator[](int i) const { - return i < count ? array[i] : Conv{}; + // Returning an empty FormatConversionCharSet has the same effect as + // short circuiting because it will never match any conversion. + constexpr FormatConversionCharSet operator[](int i) const { + return i < count ? array[i] : FormatConversionCharSet{}; } constexpr ConvList without_front() const { @@ -57,7 +59,7 @@ template <size_t count> struct ConvListT { // Make sure the array has size > 0. - Conv list[count ? count : 1]; + FormatConversionCharSet list[count ? count : 1]; }; constexpr char GetChar(string_view str, size_t index) { @@ -310,7 +312,7 @@ ConvList args_; }; -template <Conv... C> +template <FormatConversionCharSet... C> constexpr bool ValidFormatImpl(string_view format) { return FormatParser(format, {ConvListT<sizeof...(C)>{{C...}}.list, sizeof...(C)})
diff --git a/absl/strings/internal/str_format/checker_test.cc b/absl/strings/internal/str_format/checker_test.cc index 49a24b4..2334817 100644 --- a/absl/strings/internal/str_format/checker_test.cc +++ b/absl/strings/internal/str_format/checker_test.cc
@@ -24,7 +24,7 @@ } TEST(StrFormatChecker, ArgumentToConv) { - Conv conv = ArgumentToConv<std::string>(); + FormatConversionCharSet conv = ArgumentToConv<std::string>(); EXPECT_EQ(ConvToString(conv), "s"); conv = ArgumentToConv<const char*>();
diff --git a/absl/strings/internal/str_format/convert_test.cc b/absl/strings/internal/str_format/convert_test.cc index cbcd7ca..20c6229 100644 --- a/absl/strings/internal/str_format/convert_test.cc +++ b/absl/strings/internal/str_format/convert_test.cc
@@ -1,14 +1,18 @@ #include <errno.h> #include <stdarg.h> #include <stdio.h> + #include <cctype> #include <cmath> +#include <limits> #include <string> +#include <thread> // NOLINT #include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/base/internal/raw_logging.h" #include "absl/strings/internal/str_format/bind.h" +#include "absl/types/optional.h" namespace absl { ABSL_NAMESPACE_BEGIN @@ -57,7 +61,7 @@ return oss.str(); } -void StrAppend(std::string *dst, const char *format, va_list ap) { +void StrAppendV(std::string *dst, const char *format, va_list ap) { // First try with a small fixed size buffer static const int kSpaceLength = 1024; char space[kSpaceLength]; @@ -98,11 +102,18 @@ delete[] buf; } +void StrAppend(std::string *out, const char *format, ...) { + va_list ap; + va_start(ap, format); + StrAppendV(out, format, ap); + va_end(ap); +} + std::string StrPrint(const char *format, ...) { va_list ap; va_start(ap, format); std::string result; - StrAppend(&result, format, ap); + StrAppendV(&result, format, ap); va_end(ap); return result; } @@ -471,8 +482,8 @@ #endif // _MSC_VER const char *const kFormats[] = { - "%", "%.3", "%8.5", "%9", "%.60", "%.30", "%03", "%+", - "% ", "%-10", "%#15.3", "%#.0", "%.0", "%1$*2$", "%1$.*2$"}; + "%", "%.3", "%8.5", "%500", "%.5000", "%.60", "%.30", "%03", + "%+", "% ", "%-10", "%#15.3", "%#.0", "%.0", "%1$*2$", "%1$.*2$"}; std::vector<double> doubles = {0.0, -0.0, @@ -489,11 +500,6 @@ std::numeric_limits<double>::infinity(), -std::numeric_limits<double>::infinity()}; -#ifndef __APPLE__ - // Apple formats NaN differently (+nan) vs. (nan) - doubles.push_back(std::nan("")); -#endif - // Some regression tests. doubles.push_back(0.99999999999999989); @@ -512,43 +518,229 @@ } } + // Workaround libc bug. + // https://sourceware.org/bugzilla/show_bug.cgi?id=22142 + const bool gcc_bug_22142 = + StrPrint("%f", std::numeric_limits<double>::max()) != + "1797693134862315708145274237317043567980705675258449965989174768031" + "5726078002853876058955863276687817154045895351438246423432132688946" + "4182768467546703537516986049910576551282076245490090389328944075868" + "5084551339423045832369032229481658085593321233482747978262041447231" + "68738177180919299881250404026184124858368.000000"; + + if (!gcc_bug_22142) { + for (int exp = -300; exp <= 300; ++exp) { + const double all_ones_mantissa = 0x1fffffffffffff; + doubles.push_back(std::ldexp(all_ones_mantissa, exp)); + } + } + + if (gcc_bug_22142) { + for (auto &d : doubles) { + using L = std::numeric_limits<double>; + double d2 = std::abs(d); + if (d2 == L::max() || d2 == L::min() || d2 == L::denorm_min()) { + d = 0; + } + } + } + + // Remove duplicates to speed up the logic below. + std::sort(doubles.begin(), doubles.end()); + doubles.erase(std::unique(doubles.begin(), doubles.end()), doubles.end()); + +#ifndef __APPLE__ + // Apple formats NaN differently (+nan) vs. (nan) + doubles.push_back(std::nan("")); +#endif + + // Reserve the space to ensure we don't allocate memory in the output itself. + std::string str_format_result; + str_format_result.reserve(1 << 20); + std::string string_printf_result; + string_printf_result.reserve(1 << 20); + for (const char *fmt : kFormats) { for (char f : {'f', 'F', // 'g', 'G', // 'a', 'A', // 'e', 'E'}) { std::string fmt_str = std::string(fmt) + f; + + if (fmt == absl::string_view("%.5000") && f != 'f' && f != 'F') { + // This particular test takes way too long with snprintf. + // Disable for the case we are not implementing natively. + continue; + } + for (double d : doubles) { int i = -10; FormatArgImpl args[2] = {FormatArgImpl(d), FormatArgImpl(i)}; UntypedFormatSpecImpl format(fmt_str); - // We use ASSERT_EQ here because failures are usually correlated and a - // bug would print way too many failed expectations causing the test to - // time out. - ASSERT_EQ(StrPrint(fmt_str.c_str(), d, i), - FormatPack(format, absl::MakeSpan(args))) - << fmt_str << " " << StrPrint("%.18g", d) << " " - << StrPrint("%.999f", d); + + string_printf_result.clear(); + StrAppend(&string_printf_result, fmt_str.c_str(), d, i); + str_format_result.clear(); + + { + AppendPack(&str_format_result, format, absl::MakeSpan(args)); + } + + if (string_printf_result != str_format_result) { + // We use ASSERT_EQ here because failures are usually correlated and a + // bug would print way too many failed expectations causing the test + // to time out. + ASSERT_EQ(string_printf_result, str_format_result) + << fmt_str << " " << StrPrint("%.18g", d) << " " + << StrPrint("%a", d) << " " << StrPrint("%.1080f", d); + } } } } } -TEST_F(FormatConvertTest, LongDouble) { - const char *const kFormats[] = {"%", "%.3", "%8.5", "%9", - "%.60", "%+", "% ", "%-10"}; +TEST_F(FormatConvertTest, FloatRound) { + std::string s; + const auto format = [&](const char *fmt, double d) -> std::string & { + s.clear(); + FormatArgImpl args[1] = {FormatArgImpl(d)}; + AppendPack(&s, UntypedFormatSpecImpl(fmt), absl::MakeSpan(args)); +#if !defined(_MSC_VER) + // MSVC has a different rounding policy than us so we can't test our + // implementation against the native one there. + EXPECT_EQ(StrPrint(fmt, d), s); +#endif // _MSC_VER - // This value is not representable in double, but it is in long double that - // uses the extended format. - // This is to verify that we are not truncating the value mistakenly through a - // double. - long double very_precise = 10000000000000000.25L; + return s; + }; + // All of these values have to be exactly represented. + // Otherwise we might not be testing what we think we are testing. + + // These values can fit in a 64bit "fast" representation. + const double exact_value = 0.00000000000005684341886080801486968994140625; + assert(exact_value == std::pow(2, -44)); + // Round up at a 5xx. + EXPECT_EQ(format("%.13f", exact_value), "0.0000000000001"); + // Round up at a >5 + EXPECT_EQ(format("%.14f", exact_value), "0.00000000000006"); + // Round down at a <5 + EXPECT_EQ(format("%.16f", exact_value), "0.0000000000000568"); + // Nine handling + EXPECT_EQ(format("%.35f", exact_value), + "0.00000000000005684341886080801486969"); + EXPECT_EQ(format("%.36f", exact_value), + "0.000000000000056843418860808014869690"); + // Round down the last nine. + EXPECT_EQ(format("%.37f", exact_value), + "0.0000000000000568434188608080148696899"); + EXPECT_EQ(format("%.10f", 0.000003814697265625), "0.0000038147"); + // Round up the last nine + EXPECT_EQ(format("%.11f", 0.000003814697265625), "0.00000381470"); + EXPECT_EQ(format("%.12f", 0.000003814697265625), "0.000003814697"); + + // Round to even (down) + EXPECT_EQ(format("%.43f", exact_value), + "0.0000000000000568434188608080148696899414062"); + // Exact + EXPECT_EQ(format("%.44f", exact_value), + "0.00000000000005684341886080801486968994140625"); + // Round to even (up), let make the last digits 75 instead of 25 + EXPECT_EQ(format("%.43f", exact_value + std::pow(2, -43)), + "0.0000000000001705302565824240446090698242188"); + // Exact, just to check. + EXPECT_EQ(format("%.44f", exact_value + std::pow(2, -43)), + "0.00000000000017053025658242404460906982421875"); + + // This value has to be small enough that it won't fit in the uint128 + // representation for printing. + const double small_exact_value = + 0.000000000000000000000000000000000000752316384526264005099991383822237233803945956334136013765601092018187046051025390625; // NOLINT + assert(small_exact_value == std::pow(2, -120)); + // Round up at a 5xx. + EXPECT_EQ(format("%.37f", small_exact_value), + "0.0000000000000000000000000000000000008"); + // Round down at a <5 + EXPECT_EQ(format("%.38f", small_exact_value), + "0.00000000000000000000000000000000000075"); + // Round up at a >5 + EXPECT_EQ(format("%.41f", small_exact_value), + "0.00000000000000000000000000000000000075232"); + // Nine handling + EXPECT_EQ(format("%.55f", small_exact_value), + "0.0000000000000000000000000000000000007523163845262640051"); + EXPECT_EQ(format("%.56f", small_exact_value), + "0.00000000000000000000000000000000000075231638452626400510"); + EXPECT_EQ(format("%.57f", small_exact_value), + "0.000000000000000000000000000000000000752316384526264005100"); + EXPECT_EQ(format("%.58f", small_exact_value), + "0.0000000000000000000000000000000000007523163845262640051000"); + // Round down the last nine + EXPECT_EQ(format("%.59f", small_exact_value), + "0.00000000000000000000000000000000000075231638452626400509999"); + // Round up the last nine + EXPECT_EQ(format("%.79f", small_exact_value), + "0.000000000000000000000000000000000000" + "7523163845262640050999913838222372338039460"); + + // Round to even (down) + EXPECT_EQ(format("%.119f", small_exact_value), + "0.000000000000000000000000000000000000" + "75231638452626400509999138382223723380" + "394595633413601376560109201818704605102539062"); + // Exact + EXPECT_EQ(format("%.120f", small_exact_value), + "0.000000000000000000000000000000000000" + "75231638452626400509999138382223723380" + "3945956334136013765601092018187046051025390625"); + // Round to even (up), let make the last digits 75 instead of 25 + EXPECT_EQ(format("%.119f", small_exact_value + std::pow(2, -119)), + "0.000000000000000000000000000000000002" + "25694915357879201529997415146671170141" + "183786900240804129680327605456113815307617188"); + // Exact, just to check. + EXPECT_EQ(format("%.120f", small_exact_value + std::pow(2, -119)), + "0.000000000000000000000000000000000002" + "25694915357879201529997415146671170141" + "1837869002408041296803276054561138153076171875"); +} + +// We don't actually store the results. This is just to exercise the rest of the +// machinery. +struct NullSink { + friend void AbslFormatFlush(NullSink *sink, string_view str) {} +}; + +template <typename... T> +bool FormatWithNullSink(absl::string_view fmt, const T &... a) { + NullSink sink; + FormatArgImpl args[] = {FormatArgImpl(a)...}; + return FormatUntyped(&sink, UntypedFormatSpecImpl(fmt), absl::MakeSpan(args)); +} + +TEST_F(FormatConvertTest, ExtremeWidthPrecision) { + for (const char *fmt : {"f"}) { + for (double d : {1e-100, 1.0, 1e100}) { + constexpr int max = std::numeric_limits<int>::max(); + EXPECT_TRUE(FormatWithNullSink(std::string("%.*") + fmt, max, d)); + EXPECT_TRUE(FormatWithNullSink(std::string("%1.*") + fmt, max, d)); + EXPECT_TRUE(FormatWithNullSink(std::string("%*") + fmt, max, d)); + EXPECT_TRUE(FormatWithNullSink(std::string("%*.*") + fmt, max, max, d)); + } + } +} + +TEST_F(FormatConvertTest, LongDouble) { +#ifdef _MSC_VER + // MSVC has a different rounding policy than us so we can't test our + // implementation against the native one there. + return; +#endif // _MSC_VER + const char *const kFormats[] = {"%", "%.3", "%8.5", "%9", "%.5000", + "%.60", "%+", "% ", "%-10"}; std::vector<long double> doubles = { 0.0, -0.0, - very_precise, - 1 / very_precise, std::numeric_limits<long double>::max(), -std::numeric_limits<long double>::max(), std::numeric_limits<long double>::min(), @@ -556,22 +748,44 @@ std::numeric_limits<long double>::infinity(), -std::numeric_limits<long double>::infinity()}; + for (long double base : {1.L, 12.L, 123.L, 1234.L, 12345.L, 123456.L, + 1234567.L, 12345678.L, 123456789.L, 1234567890.L, + 12345678901.L, 123456789012.L, 1234567890123.L, + // This value is not representable in double, but it + // is in long double that uses the extended format. + // This is to verify that we are not truncating the + // value mistakenly through a double. + 10000000000000000.25L}) { + for (int exp : {-1000, -500, 0, 500, 1000}) { + for (int sign : {1, -1}) { + doubles.push_back(sign * std::ldexp(base, exp)); + doubles.push_back(sign / std::ldexp(base, exp)); + } + } + } + for (const char *fmt : kFormats) { for (char f : {'f', 'F', // 'g', 'G', // 'a', 'A', // 'e', 'E'}) { std::string fmt_str = std::string(fmt) + 'L' + f; + + if (fmt == absl::string_view("%.5000") && f != 'f' && f != 'F') { + // This particular test takes way too long with snprintf. + // Disable for the case we are not implementing natively. + continue; + } + for (auto d : doubles) { FormatArgImpl arg(d); UntypedFormatSpecImpl format(fmt_str); // We use ASSERT_EQ here because failures are usually correlated and a // bug would print way too many failed expectations causing the test to // time out. - ASSERT_EQ(StrPrint(fmt_str.c_str(), d), - FormatPack(format, {&arg, 1})) + ASSERT_EQ(StrPrint(fmt_str.c_str(), d), FormatPack(format, {&arg, 1})) << fmt_str << " " << StrPrint("%.18Lg", d) << " " - << StrPrint("%.999Lf", d); + << StrPrint("%La", d) << " " << StrPrint("%.1080Lf", d); } } }
diff --git a/absl/strings/internal/str_format/extension.h b/absl/strings/internal/str_format/extension.h index fb31a9d..ce78a02 100644 --- a/absl/strings/internal/str_format/extension.h +++ b/absl/strings/internal/str_format/extension.h
@@ -30,7 +30,10 @@ namespace absl { ABSL_NAMESPACE_BEGIN + namespace str_format_internal { +enum class FormatConversionCharSet : uint64_t; +enum class FormatConversionChar : uint8_t; class FormatRawSinkImpl { public: @@ -140,7 +143,7 @@ // clang-format off #define ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(X_VAL, X_SEP) \ /* text */ \ - X_VAL(c) X_SEP X_VAL(C) X_SEP X_VAL(s) X_SEP X_VAL(S) X_SEP \ + X_VAL(c) X_SEP X_VAL(s) X_SEP \ /* ints */ \ X_VAL(d) X_SEP X_VAL(i) X_SEP X_VAL(o) X_SEP \ X_VAL(u) X_SEP X_VAL(x) X_SEP X_VAL(X) X_SEP \ @@ -149,13 +152,39 @@ X_VAL(g) X_SEP X_VAL(G) X_SEP X_VAL(a) X_SEP X_VAL(A) X_SEP \ /* misc */ \ X_VAL(n) X_SEP X_VAL(p) +// clang-format on -enum class FormatConversionChar : uint8_t { - c, C, s, S, // text +// This type should not be referenced, it exists only to provide labels +// internally that match the values declared in FormatConversionChar in +// str_format.h. This is meant to allow internal libraries to use the same +// declared interface type as the public interface +// (absl::StrFormatConversionChar) while keeping the definition in a public +// header. +// Internal libraries should use the form +// `FormatConversionCharInternal::c`, `FormatConversionCharInternal::kNone` for +// comparisons. Use in switch statements is not recommended due to a bug in how +// gcc 4.9 -Wswitch handles declared but undefined enums. +struct FormatConversionCharInternal { + FormatConversionCharInternal() = delete; + + private: + // clang-format off + enum class Enum : uint8_t { + c, s, // text d, i, o, u, x, X, // int f, F, e, E, g, G, a, A, // float n, p, // misc kNone + }; + // clang-format on + public: +#define ABSL_INTERNAL_X_VAL(id) \ + static constexpr FormatConversionChar id = \ + static_cast<FormatConversionChar>(Enum::id); + ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_X_VAL, ) +#undef ABSL_INTERNAL_X_VAL + static constexpr FormatConversionChar kNone = + static_cast<FormatConversionChar>(Enum::kNone); }; // clang-format on @@ -163,56 +192,56 @@ switch (c) { #define ABSL_INTERNAL_X_VAL(id) \ case #id[0]: \ - return FormatConversionChar::id; + return FormatConversionCharInternal::id; ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_X_VAL, ) #undef ABSL_INTERNAL_X_VAL } - return FormatConversionChar::kNone; + return FormatConversionCharInternal::kNone; } inline bool FormatConversionCharIsUpper(FormatConversionChar c) { - switch (c) { - case FormatConversionChar::X: - case FormatConversionChar::F: - case FormatConversionChar::E: - case FormatConversionChar::G: - case FormatConversionChar::A: - return true; - default: - return false; + if (c == FormatConversionCharInternal::X || + c == FormatConversionCharInternal::F || + c == FormatConversionCharInternal::E || + c == FormatConversionCharInternal::G || + c == FormatConversionCharInternal::A) { + return true; + } else { + return false; } } inline bool FormatConversionCharIsFloat(FormatConversionChar c) { - switch (c) { - case FormatConversionChar::a: - case FormatConversionChar::e: - case FormatConversionChar::f: - case FormatConversionChar::g: - case FormatConversionChar::A: - case FormatConversionChar::E: - case FormatConversionChar::F: - case FormatConversionChar::G: - return true; - default: - return false; + if (c == FormatConversionCharInternal::a || + c == FormatConversionCharInternal::e || + c == FormatConversionCharInternal::f || + c == FormatConversionCharInternal::g || + c == FormatConversionCharInternal::A || + c == FormatConversionCharInternal::E || + c == FormatConversionCharInternal::F || + c == FormatConversionCharInternal::G) { + return true; + } else { + return false; } } inline char FormatConversionCharToChar(FormatConversionChar c) { - switch (c) { -#define ABSL_INTERNAL_X_VAL(e) \ - case FormatConversionChar::e: \ + if (c == FormatConversionCharInternal::kNone) { + return '\0'; + +#define ABSL_INTERNAL_X_VAL(e) \ + } else if (c == FormatConversionCharInternal::e) { \ return #e[0]; #define ABSL_INTERNAL_X_SEP - ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_X_VAL, - ABSL_INTERNAL_X_SEP) - case FormatConversionChar::kNone: - return '\0'; + ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_X_VAL, + ABSL_INTERNAL_X_SEP) + } else { + return '\0'; + } + #undef ABSL_INTERNAL_X_VAL #undef ABSL_INTERNAL_X_SEP - } - return '\0'; } // The associated char. @@ -224,7 +253,7 @@ struct FormatConversionSpecImplFriend; -class FormatConversionSpec { +class FormatConversionSpecImpl { public: // Width and precison are not specified, no flags are set. bool is_basic() const { return flags_.basic; } @@ -237,7 +266,7 @@ FormatConversionChar conversion_char() const { // Keep this field first in the struct . It generates better code when // accessing it when ConversionSpec is passed by value in registers. - static_assert(offsetof(FormatConversionSpec, conv_) == 0, ""); + static_assert(offsetof(FormatConversionSpecImpl, conv_) == 0, ""); return conv_; } @@ -248,37 +277,62 @@ // negative value. int precision() const { return precision_; } + template <typename T> + T Wrap() { + return T(*this); + } + private: friend struct str_format_internal::FormatConversionSpecImplFriend; - FormatConversionChar conv_ = FormatConversionChar::kNone; + FormatConversionChar conv_ = FormatConversionCharInternal::kNone; Flags flags_; int width_; int precision_; }; struct FormatConversionSpecImplFriend final { - static void SetFlags(Flags f, FormatConversionSpec* conv) { + static void SetFlags(Flags f, FormatConversionSpecImpl* conv) { conv->flags_ = f; } static void SetConversionChar(FormatConversionChar c, - FormatConversionSpec* conv) { + FormatConversionSpecImpl* conv) { conv->conv_ = c; } - static void SetWidth(int w, FormatConversionSpec* conv) { conv->width_ = w; } - static void SetPrecision(int p, FormatConversionSpec* conv) { + static void SetWidth(int w, FormatConversionSpecImpl* conv) { + conv->width_ = w; + } + static void SetPrecision(int p, FormatConversionSpecImpl* conv) { conv->precision_ = p; } - static std::string FlagsToString(const FormatConversionSpec& spec) { + static std::string FlagsToString(const FormatConversionSpecImpl& spec) { return spec.flags_.ToString(); } }; -constexpr uint64_t FormatConversionCharToConvValue(char conv) { +// Type safe OR operator. +// We need this for two reasons: +// 1. operator| on enums makes them decay to integers and the result is an +// integer. We need the result to stay as an enum. +// 2. We use "enum class" which would not work even if we accepted the decay. +constexpr FormatConversionCharSet FormatConversionCharSetUnion( + FormatConversionCharSet a) { + return a; +} + +template <typename... CharSet> +constexpr FormatConversionCharSet FormatConversionCharSetUnion( + FormatConversionCharSet a, CharSet... rest) { + return static_cast<FormatConversionCharSet>( + static_cast<uint64_t>(a) | + static_cast<uint64_t>(FormatConversionCharSetUnion(rest...))); +} + +constexpr uint64_t FormatConversionCharToConvInt(char conv) { return -#define ABSL_INTERNAL_CHAR_SET_CASE(c) \ - conv == #c[0] \ - ? (uint64_t{1} << (1 + static_cast<uint8_t>(FormatConversionChar::c))) \ - : +#define ABSL_INTERNAL_CHAR_SET_CASE(c) \ + conv == #c[0] ? (uint64_t{1} << (1 + static_cast<uint8_t>( \ + FormatConversionCharInternal::c))) \ + : ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_CHAR_SET_CASE, ) #undef ABSL_INTERNAL_CHAR_SET_CASE conv == '*' @@ -286,20 +340,31 @@ : 0; } -enum class FormatConversionCharSet : uint64_t { -#define ABSL_INTERNAL_CHAR_SET_CASE(c) \ - c = FormatConversionCharToConvValue(#c[0]), +constexpr FormatConversionCharSet FormatConversionCharToConvValue(char conv) { + return static_cast<FormatConversionCharSet>( + FormatConversionCharToConvInt(conv)); +} + +struct FormatConversionCharSetInternal { +#define ABSL_INTERNAL_CHAR_SET_CASE(c) \ + static constexpr FormatConversionCharSet c = \ + FormatConversionCharToConvValue(#c[0]); ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_CHAR_SET_CASE, ) #undef ABSL_INTERNAL_CHAR_SET_CASE // Used for width/precision '*' specification. - kStar = FormatConversionCharToConvValue('*'), - // Some predefined values: - kIntegral = d | i | u | o | x | X, - kFloating = a | e | f | g | A | E | F | G, - kNumeric = kIntegral | kFloating, - kString = s, - kPointer = p + static constexpr FormatConversionCharSet kStar = + FormatConversionCharToConvValue('*'); + + // Some predefined values (TODO(matthewbr), delete any that are unused). + static constexpr FormatConversionCharSet kIntegral = + FormatConversionCharSetUnion(d, i, u, o, x, X); + static constexpr FormatConversionCharSet kFloating = + FormatConversionCharSetUnion(a, e, f, g, A, E, F, G); + static constexpr FormatConversionCharSet kNumeric = + FormatConversionCharSetUnion(kIntegral, kFloating); + static constexpr FormatConversionCharSet kString = s; + static constexpr FormatConversionCharSet kPointer = p; }; // Type safe OR operator. @@ -309,8 +374,7 @@ // 2. We use "enum class" which would not work even if we accepted the decay. constexpr FormatConversionCharSet operator|(FormatConversionCharSet a, FormatConversionCharSet b) { - return FormatConversionCharSet(static_cast<uint64_t>(a) | - static_cast<uint64_t>(b)); + return FormatConversionCharSetUnion(a, b); } // Overloaded conversion functions to support absl::ParsedFormat. @@ -331,7 +395,8 @@ // Checks whether `c` exists in `set`. constexpr bool Contains(FormatConversionCharSet set, char c) { - return (static_cast<uint64_t>(set) & FormatConversionCharToConvValue(c)) != 0; + return (static_cast<uint64_t>(set) & + static_cast<uint64_t>(FormatConversionCharToConvValue(c))) != 0; } // Checks whether all the characters in `c` are contained in `set` @@ -341,30 +406,85 @@ static_cast<uint64_t>(c); } -// Return type of the AbslFormatConvert() functions. -// The FormatConversionCharSet template parameter is used to inform the -// framework of what conversion characters are supported by that -// AbslFormatConvert routine. -template <FormatConversionCharSet C> -struct FormatConvertResult { - static constexpr FormatConversionCharSet kConv = C; - bool value; -}; - -template <FormatConversionCharSet C> -constexpr FormatConversionCharSet FormatConvertResult<C>::kConv; - // Return capacity - used, clipped to a minimum of 0. inline size_t Excess(size_t used, size_t capacity) { return used < capacity ? capacity - used : 0; } -// Type alias for use during migration. -using ConversionChar = FormatConversionChar; -using ConversionSpec = FormatConversionSpec; -using Conv = FormatConversionCharSet; -template <FormatConversionCharSet C> -using ConvertResult = FormatConvertResult<C>; +class FormatConversionSpec { + public: + // Width and precison are not specified, no flags are set. + bool is_basic() const { return impl_.is_basic(); } + bool has_left_flag() const { return impl_.has_left_flag(); } + bool has_show_pos_flag() const { return impl_.has_show_pos_flag(); } + bool has_sign_col_flag() const { return impl_.has_sign_col_flag(); } + bool has_alt_flag() const { return impl_.has_alt_flag(); } + bool has_zero_flag() const { return impl_.has_zero_flag(); } + + FormatConversionChar conversion_char() const { + return impl_.conversion_char(); + } + + // Returns the specified width. If width is unspecfied, it returns a negative + // value. + int width() const { return impl_.width(); } + // Returns the specified precision. If precision is unspecfied, it returns a + // negative value. + int precision() const { return impl_.precision(); } + + private: + explicit FormatConversionSpec( + str_format_internal::FormatConversionSpecImpl impl) + : impl_(impl) {} + + friend str_format_internal::FormatConversionSpecImpl; + + absl::str_format_internal::FormatConversionSpecImpl impl_; +}; + +// clang-format off +enum class FormatConversionChar : uint8_t { + c, s, // text + d, i, o, u, x, X, // int + f, F, e, E, g, G, a, A, // float + n, p // misc +}; +// clang-format on + +enum class FormatConversionCharSet : uint64_t { + // text + c = str_format_internal::FormatConversionCharToConvInt('c'), + s = str_format_internal::FormatConversionCharToConvInt('s'), + // integer + d = str_format_internal::FormatConversionCharToConvInt('d'), + i = str_format_internal::FormatConversionCharToConvInt('i'), + o = str_format_internal::FormatConversionCharToConvInt('o'), + u = str_format_internal::FormatConversionCharToConvInt('u'), + x = str_format_internal::FormatConversionCharToConvInt('x'), + X = str_format_internal::FormatConversionCharToConvInt('X'), + // Float + f = str_format_internal::FormatConversionCharToConvInt('f'), + F = str_format_internal::FormatConversionCharToConvInt('F'), + e = str_format_internal::FormatConversionCharToConvInt('e'), + E = str_format_internal::FormatConversionCharToConvInt('E'), + g = str_format_internal::FormatConversionCharToConvInt('g'), + G = str_format_internal::FormatConversionCharToConvInt('G'), + a = str_format_internal::FormatConversionCharToConvInt('a'), + A = str_format_internal::FormatConversionCharToConvInt('A'), + // misc + n = str_format_internal::FormatConversionCharToConvInt('n'), + p = str_format_internal::FormatConversionCharToConvInt('p'), + + // Used for width/precision '*' specification. + kStar = str_format_internal::FormatConversionCharToConvInt('*'), + + // Some predefined values: + kIntegral = d | i | u | o | x | X, + kFloating = a | e | f | g | A | E | F | G, + kNumeric = kIntegral | kFloating, + kString = s, + kPointer = p, +}; } // namespace str_format_internal
diff --git a/absl/strings/internal/str_format/extension_test.cc b/absl/strings/internal/str_format/extension_test.cc index 561eaa3..0a023f9 100644 --- a/absl/strings/internal/str_format/extension_test.cc +++ b/absl/strings/internal/str_format/extension_test.cc
@@ -80,13 +80,4 @@ EXPECT_EQ(actual, expected); } } - -TEST(FormatExtensionTest, CustomSink) { - my_namespace::UserDefinedType sink; - absl::Format(&sink, "There were %04d little %s.", 3, "pigs"); - EXPECT_EQ("There were 0003 little pigs.", sink.Value()); - absl::Format(&sink, "And %-3llx bad wolf!", 1); - EXPECT_EQ("There were 0003 little pigs.And 1 bad wolf!", sink.Value()); -} - } // namespace
diff --git a/absl/strings/internal/str_format/float_conversion.cc b/absl/strings/internal/str_format/float_conversion.cc index d5a1ee4..a761a5a 100644 --- a/absl/strings/internal/str_format/float_conversion.cc +++ b/absl/strings/internal/str_format/float_conversion.cc
@@ -1,12 +1,22 @@ #include "absl/strings/internal/str_format/float_conversion.h" #include <string.h> + #include <algorithm> #include <cassert> #include <cmath> +#include <limits> #include <string> +#include "absl/base/attributes.h" #include "absl/base/config.h" +#include "absl/base/internal/bits.h" +#include "absl/base/optimization.h" +#include "absl/functional/function_ref.h" +#include "absl/meta/type_traits.h" +#include "absl/numeric/int128.h" +#include "absl/types/optional.h" +#include "absl/types/span.h" namespace absl { ABSL_NAMESPACE_BEGIN @@ -14,13 +24,644 @@ namespace { -char *CopyStringTo(string_view v, char *out) { +// The code below wants to avoid heap allocations. +// To do so it needs to allocate memory on the stack. +// `StackArray` will allocate memory on the stack in the form of a uint32_t +// array and call the provided callback with said memory. +// It will allocate memory in increments of 512 bytes. We could allocate the +// largest needed unconditionally, but that is more than we need in most of +// cases. This way we use less stack in the common cases. +class StackArray { + using Func = absl::FunctionRef<void(absl::Span<uint32_t>)>; + static constexpr size_t kStep = 512 / sizeof(uint32_t); + // 5 steps is 2560 bytes, which is enough to hold a long double with the + // largest/smallest exponents. + // The operations below will static_assert their particular maximum. + static constexpr size_t kNumSteps = 5; + + // We do not want this function to be inlined. + // Otherwise the caller will allocate the stack space unnecessarily for all + // the variants even though it only calls one. + template <size_t steps> + ABSL_ATTRIBUTE_NOINLINE static void RunWithCapacityImpl(Func f) { + uint32_t values[steps * kStep]{}; + f(absl::MakeSpan(values)); + } + + public: + static constexpr size_t kMaxCapacity = kStep * kNumSteps; + + static void RunWithCapacity(size_t capacity, Func f) { + assert(capacity <= kMaxCapacity); + const size_t step = (capacity + kStep - 1) / kStep; + assert(step <= kNumSteps); + switch (step) { + case 1: + return RunWithCapacityImpl<1>(f); + case 2: + return RunWithCapacityImpl<2>(f); + case 3: + return RunWithCapacityImpl<3>(f); + case 4: + return RunWithCapacityImpl<4>(f); + case 5: + return RunWithCapacityImpl<5>(f); + } + + assert(false && "Invalid capacity"); + } +}; + +// Calculates `10 * (*v) + carry` and stores the result in `*v` and returns +// the carry. +template <typename Int> +inline Int MultiplyBy10WithCarry(Int *v, Int carry) { + using BiggerInt = absl::conditional_t<sizeof(Int) == 4, uint64_t, uint128>; + BiggerInt tmp = 10 * static_cast<BiggerInt>(*v) + carry; + *v = static_cast<Int>(tmp); + return static_cast<Int>(tmp >> (sizeof(Int) * 8)); +} + +// Calculates `(2^64 * carry + *v) / 10`. +// Stores the quotient in `*v` and returns the remainder. +// Requires: `0 <= carry <= 9` +inline uint64_t DivideBy10WithCarry(uint64_t *v, uint64_t carry) { + constexpr uint64_t divisor = 10; + // 2^64 / divisor = chunk_quotient + chunk_remainder / divisor + constexpr uint64_t chunk_quotient = (uint64_t{1} << 63) / (divisor / 2); + constexpr uint64_t chunk_remainder = uint64_t{} - chunk_quotient * divisor; + + const uint64_t mod = *v % divisor; + const uint64_t next_carry = chunk_remainder * carry + mod; + *v = *v / divisor + carry * chunk_quotient + next_carry / divisor; + return next_carry % divisor; +} + +// Generates the decimal representation for an integer of the form `v * 2^exp`, +// where `v` and `exp` are both positive integers. +// It generates the digits from the left (ie the most significant digit first) +// to allow for direct printing into the sink. +// +// Requires `0 <= exp` and `exp <= numeric_limits<long double>::max_exponent`. +class BinaryToDecimal { + static constexpr int ChunksNeeded(int exp) { + // We will left shift a uint128 by `exp` bits, so we need `128+exp` total + // bits. Round up to 32. + // See constructor for details about adding `10%` to the value. + return (128 + exp + 31) / 32 * 11 / 10; + } + + public: + // Run the conversion for `v * 2^exp` and call `f(binary_to_decimal)`. + // This function will allocate enough stack space to perform the conversion. + static void RunConversion(uint128 v, int exp, + absl::FunctionRef<void(BinaryToDecimal)> f) { + assert(exp > 0); + assert(exp <= std::numeric_limits<long double>::max_exponent); + static_assert( + StackArray::kMaxCapacity >= + ChunksNeeded(std::numeric_limits<long double>::max_exponent), + ""); + + StackArray::RunWithCapacity( + ChunksNeeded(exp), + [=](absl::Span<uint32_t> input) { f(BinaryToDecimal(input, v, exp)); }); + } + + int TotalDigits() const { + return static_cast<int>((decimal_end_ - decimal_start_) * kDigitsPerChunk + + CurrentDigits().size()); + } + + // See the current block of digits. + absl::string_view CurrentDigits() const { + return absl::string_view(digits_ + kDigitsPerChunk - size_, size_); + } + + // Advance the current view of digits. + // Returns `false` when no more digits are available. + bool AdvanceDigits() { + if (decimal_start_ >= decimal_end_) return false; + + uint32_t w = data_[decimal_start_++]; + for (size_ = 0; size_ < kDigitsPerChunk; w /= 10) { + digits_[kDigitsPerChunk - ++size_] = w % 10 + '0'; + } + return true; + } + + private: + BinaryToDecimal(absl::Span<uint32_t> data, uint128 v, int exp) : data_(data) { + // We need to print the digits directly into the sink object without + // buffering them all first. To do this we need two things: + // - to know the total number of digits to do padding when necessary + // - to generate the decimal digits from the left. + // + // In order to do this, we do a two pass conversion. + // On the first pass we convert the binary representation of the value into + // a decimal representation in which each uint32_t chunk holds up to 9 + // decimal digits. In the second pass we take each decimal-holding-uint32_t + // value and generate the ascii decimal digits into `digits_`. + // + // The binary and decimal representations actually share the same memory + // region. As we go converting the chunks from binary to decimal we free + // them up and reuse them for the decimal representation. One caveat is that + // the decimal representation is around 7% less efficient in space than the + // binary one. We allocate an extra 10% memory to account for this. See + // ChunksNeeded for this calculation. + int chunk_index = exp / 32; + decimal_start_ = decimal_end_ = ChunksNeeded(exp); + const int offset = exp % 32; + // Left shift v by exp bits. + data_[chunk_index] = static_cast<uint32_t>(v << offset); + for (v >>= (32 - offset); v; v >>= 32) + data_[++chunk_index] = static_cast<uint32_t>(v); + + while (chunk_index >= 0) { + // While we have more than one chunk available, go in steps of 1e9. + // `data_[chunk_index]` holds the highest non-zero binary chunk, so keep + // the variable updated. + uint32_t carry = 0; + for (int i = chunk_index; i >= 0; --i) { + uint64_t tmp = uint64_t{data_[i]} + (uint64_t{carry} << 32); + data_[i] = static_cast<uint32_t>(tmp / uint64_t{1000000000}); + carry = static_cast<uint32_t>(tmp % uint64_t{1000000000}); + } + + // If the highest chunk is now empty, remove it from view. + if (data_[chunk_index] == 0) --chunk_index; + + --decimal_start_; + assert(decimal_start_ != chunk_index); + data_[decimal_start_] = carry; + } + + // Fill the first set of digits. The first chunk might not be complete, so + // handle differently. + for (uint32_t first = data_[decimal_start_++]; first != 0; first /= 10) { + digits_[kDigitsPerChunk - ++size_] = first % 10 + '0'; + } + } + + private: + static constexpr size_t kDigitsPerChunk = 9; + + int decimal_start_; + int decimal_end_; + + char digits_[kDigitsPerChunk]; + int size_ = 0; + + absl::Span<uint32_t> data_; +}; + +// Converts a value of the form `x * 2^-exp` into a sequence of decimal digits. +// Requires `-exp < 0` and +// `-exp >= limits<long double>::min_exponent - limits<long double>::digits`. +class FractionalDigitGenerator { + public: + // Run the conversion for `v * 2^exp` and call `f(generator)`. + // This function will allocate enough stack space to perform the conversion. + static void RunConversion( + uint128 v, int exp, absl::FunctionRef<void(FractionalDigitGenerator)> f) { + assert(-exp < 0); + assert(-exp >= std::numeric_limits<long double>::min_exponent - 128); + static_assert( + StackArray::kMaxCapacity >= + (128 - std::numeric_limits<long double>::min_exponent + 31) / 32, + ""); + StackArray::RunWithCapacity((exp + 31) / 32, + [=](absl::Span<uint32_t> input) { + f(FractionalDigitGenerator(input, v, exp)); + }); + } + + // Returns true if there are any more non-zero digits left. + bool HasMoreDigits() const { return next_digit_ != 0 || chunk_index_ >= 0; } + + // Returns true if the remainder digits are greater than 5000... + bool IsGreaterThanHalf() const { + return next_digit_ > 5 || (next_digit_ == 5 && chunk_index_ >= 0); + } + // Returns true if the remainder digits are exactly 5000... + bool IsExactlyHalf() const { return next_digit_ == 5 && chunk_index_ < 0; } + + struct Digits { + int digit_before_nine; + int num_nines; + }; + + // Get the next set of digits. + // They are composed by a non-9 digit followed by a runs of zero or more 9s. + Digits GetDigits() { + Digits digits{next_digit_, 0}; + + next_digit_ = GetOneDigit(); + while (next_digit_ == 9) { + ++digits.num_nines; + next_digit_ = GetOneDigit(); + } + + return digits; + } + + private: + // Return the next digit. + int GetOneDigit() { + if (chunk_index_ < 0) return 0; + + uint32_t carry = 0; + for (int i = chunk_index_; i >= 0; --i) { + carry = MultiplyBy10WithCarry(&data_[i], carry); + } + // If the lowest chunk is now empty, remove it from view. + if (data_[chunk_index_] == 0) --chunk_index_; + return carry; + } + + FractionalDigitGenerator(absl::Span<uint32_t> data, uint128 v, int exp) + : chunk_index_(exp / 32), data_(data) { + const int offset = exp % 32; + // Right shift `v` by `exp` bits. + data_[chunk_index_] = static_cast<uint32_t>(v << (32 - offset)); + v >>= offset; + // Make sure we don't overflow the data. We already calculated that + // non-zero bits fit, so we might not have space for leading zero bits. + for (int pos = chunk_index_; v; v >>= 32) + data_[--pos] = static_cast<uint32_t>(v); + + // Fill next_digit_, as GetDigits expects it to be populated always. + next_digit_ = GetOneDigit(); + } + + int next_digit_; + int chunk_index_; + absl::Span<uint32_t> data_; +}; + +// Count the number of leading zero bits. +int LeadingZeros(uint64_t v) { return base_internal::CountLeadingZeros64(v); } +int LeadingZeros(uint128 v) { + auto high = static_cast<uint64_t>(v >> 64); + auto low = static_cast<uint64_t>(v); + return high != 0 ? base_internal::CountLeadingZeros64(high) + : 64 + base_internal::CountLeadingZeros64(low); +} + +// Round up the text digits starting at `p`. +// The buffer must have an extra digit that is known to not need rounding. +// This is done below by having an extra '0' digit on the left. +void RoundUp(char *p) { + while (*p == '9' || *p == '.') { + if (*p == '9') *p = '0'; + --p; + } + ++*p; +} + +// Check the previous digit and round up or down to follow the round-to-even +// policy. +void RoundToEven(char *p) { + if (*p == '.') --p; + if (*p % 2 == 1) RoundUp(p); +} + +// Simple integral decimal digit printing for values that fit in 64-bits. +// Returns the pointer to the last written digit. +char *PrintIntegralDigitsFromRightFast(uint64_t v, char *p) { + do { + *--p = DivideBy10WithCarry(&v, 0) + '0'; + } while (v != 0); + return p; +} + +// Simple integral decimal digit printing for values that fit in 128-bits. +// Returns the pointer to the last written digit. +char *PrintIntegralDigitsFromRightFast(uint128 v, char *p) { + auto high = static_cast<uint64_t>(v >> 64); + auto low = static_cast<uint64_t>(v); + + while (high != 0) { + uint64_t carry = DivideBy10WithCarry(&high, 0); + carry = DivideBy10WithCarry(&low, carry); + *--p = carry + '0'; + } + return PrintIntegralDigitsFromRightFast(low, p); +} + +// Simple fractional decimal digit printing for values that fir in 64-bits after +// shifting. +// Performs rounding if necessary to fit within `precision`. +// Returns the pointer to one after the last character written. +char *PrintFractionalDigitsFast(uint64_t v, char *start, int exp, + int precision) { + char *p = start; + v <<= (64 - exp); + while (precision > 0) { + if (!v) return p; + *p++ = MultiplyBy10WithCarry(&v, uint64_t{0}) + '0'; + --precision; + } + + // We need to round. + if (v < 0x8000000000000000) { + // We round down, so nothing to do. + } else if (v > 0x8000000000000000) { + // We round up. + RoundUp(p - 1); + } else { + RoundToEven(p - 1); + } + + assert(precision == 0); + // Precision can only be zero here. + return p; +} + +// Simple fractional decimal digit printing for values that fir in 128-bits +// after shifting. +// Performs rounding if necessary to fit within `precision`. +// Returns the pointer to one after the last character written. +char *PrintFractionalDigitsFast(uint128 v, char *start, int exp, + int precision) { + char *p = start; + v <<= (128 - exp); + auto high = static_cast<uint64_t>(v >> 64); + auto low = static_cast<uint64_t>(v); + + // While we have digits to print and `low` is not empty, do the long + // multiplication. + while (precision > 0 && low != 0) { + uint64_t carry = MultiplyBy10WithCarry(&low, uint64_t{0}); + carry = MultiplyBy10WithCarry(&high, carry); + + *p++ = carry + '0'; + --precision; + } + + // Now `low` is empty, so use a faster approach for the rest of the digits. + // This block is pretty much the same as the main loop for the 64-bit case + // above. + while (precision > 0) { + if (!high) return p; + *p++ = MultiplyBy10WithCarry(&high, uint64_t{0}) + '0'; + --precision; + } + + // We need to round. + if (high < 0x8000000000000000) { + // We round down, so nothing to do. + } else if (high > 0x8000000000000000 || low != 0) { + // We round up. + RoundUp(p - 1); + } else { + RoundToEven(p - 1); + } + + assert(precision == 0); + // Precision can only be zero here. + return p; +} + +struct FormatState { + char sign_char; + int precision; + const FormatConversionSpecImpl &conv; + FormatSinkImpl *sink; + + // In `alt` mode (flag #) we keep the `.` even if there are no fractional + // digits. In non-alt mode, we strip it. + bool ShouldPrintDot() const { return precision != 0 || conv.has_alt_flag(); } +}; + +struct Padding { + int left_spaces; + int zeros; + int right_spaces; +}; + +Padding ExtraWidthToPadding(size_t total_size, const FormatState &state) { + if (state.conv.width() < 0 || state.conv.width() <= total_size) + return {0, 0, 0}; + int missing_chars = state.conv.width() - total_size; + if (state.conv.has_left_flag()) { + return {0, 0, missing_chars}; + } else if (state.conv.has_zero_flag()) { + return {0, missing_chars, 0}; + } else { + return {missing_chars, 0, 0}; + } +} + +void FinalPrint(absl::string_view data, int trailing_zeros, + const FormatState &state) { + if (state.conv.width() < 0) { + // No width specified. Fast-path. + if (state.sign_char != '\0') state.sink->Append(1, state.sign_char); + state.sink->Append(data); + state.sink->Append(trailing_zeros, '0'); + return; + } + + auto padding = + ExtraWidthToPadding((state.sign_char != '\0' ? 1 : 0) + data.size() + + static_cast<size_t>(trailing_zeros), + state); + + state.sink->Append(padding.left_spaces, ' '); + if (state.sign_char != '\0') state.sink->Append(1, state.sign_char); + state.sink->Append(padding.zeros, '0'); + state.sink->Append(data); + state.sink->Append(trailing_zeros, '0'); + state.sink->Append(padding.right_spaces, ' '); +} + +// Fastpath %f formatter for when the shifted value fits in a simple integral +// type. +// Prints `v*2^exp` with the options from `state`. +template <typename Int> +void FormatFFast(Int v, int exp, const FormatState &state) { + constexpr int input_bits = sizeof(Int) * 8; + + static constexpr size_t integral_size = + /* in case we need to round up an extra digit */ 1 + + /* decimal digits for uint128 */ 40 + 1; + char buffer[integral_size + /* . */ 1 + /* max digits uint128 */ 128]; + buffer[integral_size] = '.'; + char *const integral_digits_end = buffer + integral_size; + char *integral_digits_start; + char *const fractional_digits_start = buffer + integral_size + 1; + char *fractional_digits_end = fractional_digits_start; + + if (exp >= 0) { + const int total_bits = input_bits - LeadingZeros(v) + exp; + integral_digits_start = + total_bits <= 64 + ? PrintIntegralDigitsFromRightFast(static_cast<uint64_t>(v) << exp, + integral_digits_end) + : PrintIntegralDigitsFromRightFast(static_cast<uint128>(v) << exp, + integral_digits_end); + } else { + exp = -exp; + + integral_digits_start = PrintIntegralDigitsFromRightFast( + exp < input_bits ? v >> exp : 0, integral_digits_end); + // PrintFractionalDigits may pull a carried 1 all the way up through the + // integral portion. + integral_digits_start[-1] = '0'; + + fractional_digits_end = + exp <= 64 ? PrintFractionalDigitsFast(v, fractional_digits_start, exp, + state.precision) + : PrintFractionalDigitsFast(static_cast<uint128>(v), + fractional_digits_start, exp, + state.precision); + // There was a carry, so include the first digit too. + if (integral_digits_start[-1] != '0') --integral_digits_start; + } + + size_t size = fractional_digits_end - integral_digits_start; + + // In `alt` mode (flag #) we keep the `.` even if there are no fractional + // digits. In non-alt mode, we strip it. + if (!state.ShouldPrintDot()) --size; + FinalPrint(absl::string_view(integral_digits_start, size), + static_cast<int>(state.precision - (fractional_digits_end - + fractional_digits_start)), + state); +} + +// Slow %f formatter for when the shifted value does not fit in a uint128, and +// `exp > 0`. +// Prints `v*2^exp` with the options from `state`. +// This one is guaranteed to not have fractional digits, so we don't have to +// worry about anything after the `.`. +void FormatFPositiveExpSlow(uint128 v, int exp, const FormatState &state) { + BinaryToDecimal::RunConversion(v, exp, [&](BinaryToDecimal btd) { + const size_t total_digits = + btd.TotalDigits() + + (state.ShouldPrintDot() ? static_cast<size_t>(state.precision) + 1 : 0); + + const auto padding = ExtraWidthToPadding( + total_digits + (state.sign_char != '\0' ? 1 : 0), state); + + state.sink->Append(padding.left_spaces, ' '); + if (state.sign_char != '\0') state.sink->Append(1, state.sign_char); + state.sink->Append(padding.zeros, '0'); + + do { + state.sink->Append(btd.CurrentDigits()); + } while (btd.AdvanceDigits()); + + if (state.ShouldPrintDot()) state.sink->Append(1, '.'); + state.sink->Append(state.precision, '0'); + state.sink->Append(padding.right_spaces, ' '); + }); +} + +// Slow %f formatter for when the shifted value does not fit in a uint128, and +// `exp < 0`. +// Prints `v*2^exp` with the options from `state`. +// This one is guaranteed to be < 1.0, so we don't have to worry about integral +// digits. +void FormatFNegativeExpSlow(uint128 v, int exp, const FormatState &state) { + const size_t total_digits = + /* 0 */ 1 + + (state.ShouldPrintDot() ? static_cast<size_t>(state.precision) + 1 : 0); + auto padding = + ExtraWidthToPadding(total_digits + (state.sign_char ? 1 : 0), state); + padding.zeros += 1; + state.sink->Append(padding.left_spaces, ' '); + if (state.sign_char != '\0') state.sink->Append(1, state.sign_char); + state.sink->Append(padding.zeros, '0'); + + if (state.ShouldPrintDot()) state.sink->Append(1, '.'); + + // Print digits + int digits_to_go = state.precision; + + FractionalDigitGenerator::RunConversion( + v, exp, [&](FractionalDigitGenerator digit_gen) { + // There are no digits to print here. + if (state.precision == 0) return; + + // We go one digit at a time, while keeping track of runs of nines. + // The runs of nines are used to perform rounding when necessary. + + while (digits_to_go > 0 && digit_gen.HasMoreDigits()) { + auto digits = digit_gen.GetDigits(); + + // Now we have a digit and a run of nines. + // See if we can print them all. + if (digits.num_nines + 1 < digits_to_go) { + // We don't have to round yet, so print them. + state.sink->Append(1, digits.digit_before_nine + '0'); + state.sink->Append(digits.num_nines, '9'); + digits_to_go -= digits.num_nines + 1; + + } else { + // We can't print all the nines, see where we have to truncate. + + bool round_up = false; + if (digits.num_nines + 1 > digits_to_go) { + // We round up at a nine. No need to print them. + round_up = true; + } else { + // We can fit all the nines, but truncate just after it. + if (digit_gen.IsGreaterThanHalf()) { + round_up = true; + } else if (digit_gen.IsExactlyHalf()) { + // Round to even + round_up = + digits.num_nines != 0 || digits.digit_before_nine % 2 == 1; + } + } + + if (round_up) { + state.sink->Append(1, digits.digit_before_nine + '1'); + --digits_to_go; + // The rest will be zeros. + } else { + state.sink->Append(1, digits.digit_before_nine + '0'); + state.sink->Append(digits_to_go - 1, '9'); + digits_to_go = 0; + } + return; + } + } + }); + + state.sink->Append(digits_to_go, '0'); + state.sink->Append(padding.right_spaces, ' '); +} + +template <typename Int> +void FormatF(Int mantissa, int exp, const FormatState &state) { + if (exp >= 0) { + const int total_bits = sizeof(Int) * 8 - LeadingZeros(mantissa) + exp; + + // Fallback to the slow stack-based approach if we can't do it in a 64 or + // 128 bit state. + if (ABSL_PREDICT_FALSE(total_bits > 128)) { + return FormatFPositiveExpSlow(mantissa, exp, state); + } + } else { + // Fallback to the slow stack-based approach if we can't do it in a 64 or + // 128 bit state. + if (ABSL_PREDICT_FALSE(exp < -128)) { + return FormatFNegativeExpSlow(mantissa, -exp, state); + } + } + return FormatFFast(mantissa, exp, state); +} + +char *CopyStringTo(absl::string_view v, char *out) { std::memcpy(out, v.data(), v.size()); return out + v.size(); } template <typename Float> -bool FallbackToSnprintf(const Float v, const ConversionSpec &conv, +bool FallbackToSnprintf(const Float v, const FormatConversionSpecImpl &conv, FormatSinkImpl *sink) { int w = conv.width() >= 0 ? conv.width() : 0; int p = conv.precision() >= 0 ? conv.precision() : -1; @@ -38,12 +679,12 @@ assert(fp < fmt + sizeof(fmt)); } std::string space(512, '\0'); - string_view result; + absl::string_view result; while (true) { int n = snprintf(&space[0], space.size(), fmt, w, p, v); if (n < 0) return false; if (static_cast<size_t>(n) < space.size()) { - result = string_view(space.data(), n); + result = absl::string_view(space.data(), n); break; } space.resize(n + 1); @@ -96,9 +737,10 @@ // Otherwise, return false. template <typename Float> bool ConvertNonNumericFloats(char sign_char, Float v, - const ConversionSpec &conv, FormatSinkImpl *sink) { + const FormatConversionSpecImpl &conv, + FormatSinkImpl *sink) { char text[4], *ptr = text; - if (sign_char) *ptr++ = sign_char; + if (sign_char != '\0') *ptr++ = sign_char; if (std::isnan(v)) { ptr = std::copy_n( FormatConversionCharIsUpper(conv.conversion_char()) ? "NAN" : "nan", 3, @@ -172,7 +814,12 @@ template <typename Float> struct Decomposed { - Float mantissa; + using MantissaType = + absl::conditional_t<std::is_same<long double, Float>::value, uint128, + uint64_t>; + static_assert(std::numeric_limits<Float>::digits <= sizeof(MantissaType) * 8, + ""); + MantissaType mantissa; int exponent; }; @@ -183,7 +830,8 @@ Float m = std::frexp(v, &exp); m = std::ldexp(m, std::numeric_limits<Float>::digits); exp -= std::numeric_limits<Float>::digits; - return {m, exp}; + + return {static_cast<typename Decomposed<Float>::MantissaType>(m), exp}; } // Print 'digits' as decimal. @@ -352,8 +1000,9 @@ return false; } -void WriteBufferToSink(char sign_char, string_view str, - const ConversionSpec &conv, FormatSinkImpl *sink) { +void WriteBufferToSink(char sign_char, absl::string_view str, + const FormatConversionSpecImpl &conv, + FormatSinkImpl *sink) { int left_spaces = 0, zeros = 0, right_spaces = 0; int missing_chars = conv.width() >= 0 ? std::max(conv.width() - static_cast<int>(str.size()) - @@ -369,14 +1018,14 @@ } sink->Append(left_spaces, ' '); - if (sign_char) sink->Append(1, sign_char); + if (sign_char != '\0') sink->Append(1, sign_char); sink->Append(zeros, '0'); sink->Append(str); sink->Append(right_spaces, ' '); } template <typename Float> -bool FloatToSink(const Float v, const ConversionSpec &conv, +bool FloatToSink(const Float v, const FormatConversionSpecImpl &conv, FormatSinkImpl *sink) { // Print the sign or the sign column. Float abs_v = v; @@ -403,92 +1052,89 @@ Buffer buffer; - switch (conv.conversion_char()) { - case ConversionChar::f: - case ConversionChar::F: - if (!FloatToBuffer<FormatStyle::Fixed>(decomposed, precision, &buffer, - nullptr)) { - return FallbackToSnprintf(v, conv, sink); - } - if (!conv.has_alt_flag() && buffer.back() == '.') buffer.pop_back(); - break; + FormatConversionChar c = conv.conversion_char(); - case ConversionChar::e: - case ConversionChar::E: - if (!FloatToBuffer<FormatStyle::Precision>(decomposed, precision, &buffer, - &exp)) { - return FallbackToSnprintf(v, conv, sink); + if (c == FormatConversionCharInternal::f || + c == FormatConversionCharInternal::F) { + FormatF(decomposed.mantissa, decomposed.exponent, + {sign_char, precision, conv, sink}); + return true; + } else if (c == FormatConversionCharInternal::e || + c == FormatConversionCharInternal::E) { + if (!FloatToBuffer<FormatStyle::Precision>(decomposed, precision, &buffer, + &exp)) { + return FallbackToSnprintf(v, conv, sink); + } + if (!conv.has_alt_flag() && buffer.back() == '.') buffer.pop_back(); + PrintExponent( + exp, FormatConversionCharIsUpper(conv.conversion_char()) ? 'E' : 'e', + &buffer); + } else if (c == FormatConversionCharInternal::g || + c == FormatConversionCharInternal::G) { + precision = std::max(0, precision - 1); + if (!FloatToBuffer<FormatStyle::Precision>(decomposed, precision, &buffer, + &exp)) { + return FallbackToSnprintf(v, conv, sink); + } + if (precision + 1 > exp && exp >= -4) { + if (exp < 0) { + // Have 1.23456, needs 0.00123456 + // Move the first digit + buffer.begin[1] = *buffer.begin; + // Add some zeros + for (; exp < -1; ++exp) *buffer.begin-- = '0'; + *buffer.begin-- = '.'; + *buffer.begin = '0'; + } else if (exp > 0) { + // Have 1.23456, needs 1234.56 + // Move the '.' exp positions to the right. + std::rotate(buffer.begin + 1, buffer.begin + 2, buffer.begin + exp + 2); } - if (!conv.has_alt_flag() && buffer.back() == '.') buffer.pop_back(); + exp = 0; + } + if (!conv.has_alt_flag()) { + while (buffer.back() == '0') buffer.pop_back(); + if (buffer.back() == '.') buffer.pop_back(); + } + if (exp) { PrintExponent( exp, FormatConversionCharIsUpper(conv.conversion_char()) ? 'E' : 'e', &buffer); - break; - - case ConversionChar::g: - case ConversionChar::G: - precision = std::max(0, precision - 1); - if (!FloatToBuffer<FormatStyle::Precision>(decomposed, precision, &buffer, - &exp)) { - return FallbackToSnprintf(v, conv, sink); - } - if (precision + 1 > exp && exp >= -4) { - if (exp < 0) { - // Have 1.23456, needs 0.00123456 - // Move the first digit - buffer.begin[1] = *buffer.begin; - // Add some zeros - for (; exp < -1; ++exp) *buffer.begin-- = '0'; - *buffer.begin-- = '.'; - *buffer.begin = '0'; - } else if (exp > 0) { - // Have 1.23456, needs 1234.56 - // Move the '.' exp positions to the right. - std::rotate(buffer.begin + 1, buffer.begin + 2, - buffer.begin + exp + 2); - } - exp = 0; - } - if (!conv.has_alt_flag()) { - while (buffer.back() == '0') buffer.pop_back(); - if (buffer.back() == '.') buffer.pop_back(); - } - if (exp) { - PrintExponent( - exp, - FormatConversionCharIsUpper(conv.conversion_char()) ? 'E' : 'e', - &buffer); - } - break; - - case ConversionChar::a: - case ConversionChar::A: - return FallbackToSnprintf(v, conv, sink); - - default: - return false; + } + } else if (c == FormatConversionCharInternal::a || + c == FormatConversionCharInternal::A) { + return FallbackToSnprintf(v, conv, sink); + } else { + return false; } WriteBufferToSink(sign_char, - string_view(buffer.begin, buffer.end - buffer.begin), conv, - sink); + absl::string_view(buffer.begin, buffer.end - buffer.begin), + conv, sink); return true; } } // namespace -bool ConvertFloatImpl(long double v, const ConversionSpec &conv, +bool ConvertFloatImpl(long double v, const FormatConversionSpecImpl &conv, + FormatSinkImpl *sink) { + if (std::numeric_limits<long double>::digits == + 2 * std::numeric_limits<double>::digits) { + // This is the `double-double` representation of `long double`. + // We do not handle it natively. Fallback to snprintf. + return FallbackToSnprintf(v, conv, sink); + } + + return FloatToSink(v, conv, sink); +} + +bool ConvertFloatImpl(float v, const FormatConversionSpecImpl &conv, FormatSinkImpl *sink) { return FloatToSink(v, conv, sink); } -bool ConvertFloatImpl(float v, const ConversionSpec &conv, - FormatSinkImpl *sink) { - return FloatToSink(v, conv, sink); -} - -bool ConvertFloatImpl(double v, const ConversionSpec &conv, +bool ConvertFloatImpl(double v, const FormatConversionSpecImpl &conv, FormatSinkImpl *sink) { return FloatToSink(v, conv, sink); }
diff --git a/absl/strings/internal/str_format/float_conversion.h b/absl/strings/internal/str_format/float_conversion.h index 49a6a63..e78bc19 100644 --- a/absl/strings/internal/str_format/float_conversion.h +++ b/absl/strings/internal/str_format/float_conversion.h
@@ -7,13 +7,13 @@ ABSL_NAMESPACE_BEGIN namespace str_format_internal { -bool ConvertFloatImpl(float v, const ConversionSpec &conv, +bool ConvertFloatImpl(float v, const FormatConversionSpecImpl &conv, FormatSinkImpl *sink); -bool ConvertFloatImpl(double v, const ConversionSpec &conv, +bool ConvertFloatImpl(double v, const FormatConversionSpecImpl &conv, FormatSinkImpl *sink); -bool ConvertFloatImpl(long double v, const ConversionSpec &conv, +bool ConvertFloatImpl(long double v, const FormatConversionSpecImpl &conv, FormatSinkImpl *sink); } // namespace str_format_internal
diff --git a/absl/strings/internal/str_format/parser.cc b/absl/strings/internal/str_format/parser.cc index aab68db..cc55dfa 100644 --- a/absl/strings/internal/str_format/parser.cc +++ b/absl/strings/internal/str_format/parser.cc
@@ -17,7 +17,7 @@ ABSL_NAMESPACE_BEGIN namespace str_format_internal { -using CC = ConversionChar; +using CC = FormatConversionCharInternal; using LM = LengthMod; ABSL_CONST_INIT const ConvTag kTags[256] = { @@ -29,9 +29,9 @@ {}, {}, {}, {}, {}, {}, {}, {}, // 28-2f {}, {}, {}, {}, {}, {}, {}, {}, // 30-37 {}, {}, {}, {}, {}, {}, {}, {}, // 38-3f - {}, CC::A, {}, CC::C, {}, CC::E, CC::F, CC::G, // @ABCDEFG + {}, CC::A, {}, {}, {}, CC::E, CC::F, CC::G, // @ABCDEFG {}, {}, {}, {}, LM::L, {}, {}, {}, // HIJKLMNO - {}, {}, {}, CC::S, {}, {}, {}, {}, // PQRSTUVW + {}, {}, {}, {}, {}, {}, {}, {}, // PQRSTUVW CC::X, {}, {}, {}, {}, {}, {}, {}, // XYZ[\]^_ {}, CC::a, {}, CC::c, CC::d, CC::e, CC::f, CC::g, // `abcdefg LM::h, CC::i, LM::j, {}, LM::l, {}, CC::n, CC::o, // hijklmno @@ -296,15 +296,17 @@ char* data_pos; }; -ParsedFormatBase::ParsedFormatBase(string_view format, bool allow_ignored, - std::initializer_list<Conv> convs) +ParsedFormatBase::ParsedFormatBase( + string_view format, bool allow_ignored, + std::initializer_list<FormatConversionCharSet> convs) : data_(format.empty() ? nullptr : new char[format.size()]) { has_error_ = !ParseFormatString(format, ParsedFormatConsumer(this)) || !MatchesConversions(allow_ignored, convs); } bool ParsedFormatBase::MatchesConversions( - bool allow_ignored, std::initializer_list<Conv> convs) const { + bool allow_ignored, + std::initializer_list<FormatConversionCharSet> convs) const { std::unordered_set<int> used; auto add_if_valid_conv = [&](int pos, char c) { if (static_cast<size_t>(pos) > convs.size() ||
diff --git a/absl/strings/internal/str_format/parser.h b/absl/strings/internal/str_format/parser.h index 7d96651..fffed04 100644 --- a/absl/strings/internal/str_format/parser.h +++ b/absl/strings/internal/str_format/parser.h
@@ -67,7 +67,7 @@ Flags flags; LengthMod length_mod = LengthMod::none; - ConversionChar conv = FormatConversionChar::kNone; + FormatConversionChar conv = FormatConversionCharInternal::kNone; }; // Consume conversion spec prefix (not including '%') of [p, end) if valid. @@ -83,7 +83,7 @@ // conversions. class ConvTag { public: - constexpr ConvTag(ConversionChar conversion_char) // NOLINT + constexpr ConvTag(FormatConversionChar conversion_char) // NOLINT : tag_(static_cast<int8_t>(conversion_char)) {} // We invert the length modifiers to make them negative so that we can easily // test for them. @@ -94,9 +94,9 @@ bool is_conv() const { return tag_ >= 0; } bool is_length() const { return tag_ < 0 && tag_ != -128; } - ConversionChar as_conv() const { + FormatConversionChar as_conv() const { assert(is_conv()); - return static_cast<ConversionChar>(tag_); + return static_cast<FormatConversionChar>(tag_); } LengthMod as_length() const { assert(is_length()); @@ -186,8 +186,9 @@ class ParsedFormatBase { public: - explicit ParsedFormatBase(string_view format, bool allow_ignored, - std::initializer_list<Conv> convs); + explicit ParsedFormatBase( + string_view format, bool allow_ignored, + std::initializer_list<FormatConversionCharSet> convs); ParsedFormatBase(const ParsedFormatBase& other) { *this = other; } @@ -234,8 +235,9 @@ private: // Returns whether the conversions match and if !allow_ignored it verifies // that all conversions are used by the format. - bool MatchesConversions(bool allow_ignored, - std::initializer_list<Conv> convs) const; + bool MatchesConversions( + bool allow_ignored, + std::initializer_list<FormatConversionCharSet> convs) const; struct ParsedFormatConsumer; @@ -280,7 +282,7 @@ // This is the only API that allows the user to pass a runtime specified format // string. These factory functions will return NULL if the format does not match // the conversions requested by the user. -template <str_format_internal::Conv... C> +template <FormatConversionCharSet... C> class ExtendedParsedFormat : public str_format_internal::ParsedFormatBase { public: explicit ExtendedParsedFormat(string_view format)
diff --git a/absl/strings/internal/str_format/parser_test.cc b/absl/strings/internal/str_format/parser_test.cc index 51eb53f..5aced98 100644 --- a/absl/strings/internal/str_format/parser_test.cc +++ b/absl/strings/internal/str_format/parser_test.cc
@@ -41,23 +41,23 @@ TEST(ConversionCharTest, Names) { struct Expectation { - ConversionChar id; + FormatConversionChar id; char name; }; // clang-format off const Expectation kExpect[] = { -#define X(c) {ConversionChar::c, #c[0]} - X(c), X(C), X(s), X(S), // text +#define X(c) {FormatConversionCharInternal::c, #c[0]} + X(c), X(s), // text X(d), X(i), X(o), X(u), X(x), X(X), // int X(f), X(F), X(e), X(E), X(g), X(G), X(a), X(A), // float X(n), X(p), // misc #undef X - {ConversionChar::kNone, '\0'}, + {FormatConversionCharInternal::kNone, '\0'}, }; // clang-format on for (auto e : kExpect) { SCOPED_TRACE(e.name); - ConversionChar v = e.id; + FormatConversionChar v = e.id; EXPECT_EQ(e.name, FormatConversionCharToChar(v)); } } @@ -349,7 +349,8 @@ ParsedFormatBase p2 = p1; // copy construct (empty) EXPECT_EQ(SummarizeParsedFormat(p1), SummarizeParsedFormat(p2)); - p1 = ParsedFormatBase("hello%s", true, {Conv::s}); // move assign + p1 = ParsedFormatBase("hello%s", true, + {FormatConversionCharSetInternal::s}); // move assign EXPECT_EQ("[hello]{s:1$s}", SummarizeParsedFormat(p1)); ParsedFormatBase p3 = p1; // copy construct (nonempty) @@ -367,7 +368,7 @@ struct ExpectParse { const char* in; - std::initializer_list<Conv> conv_set; + std::initializer_list<FormatConversionCharSet> conv_set; const char* out; }; @@ -377,9 +378,9 @@ const ExpectParse kExpect[] = { {"", {}, ""}, {"ab", {}, "[ab]"}, - {"a%d", {Conv::d}, "[a]{d:1$d}"}, - {"a%+d", {Conv::d}, "[a]{+d:1$d}"}, - {"a% d", {Conv::d}, "[a]{ d:1$d}"}, + {"a%d", {FormatConversionCharSetInternal::d}, "[a]{d:1$d}"}, + {"a%+d", {FormatConversionCharSetInternal::d}, "[a]{+d:1$d}"}, + {"a% d", {FormatConversionCharSetInternal::d}, "[a]{ d:1$d}"}, {"a%b %d", {}, "[a]!"}, // stop after error }; for (const auto& e : kExpect) { @@ -391,13 +392,13 @@ TEST_F(ParsedFormatTest, ParsingFlagOrder) { const ExpectParse kExpect[] = { - {"a%+ 0d", {Conv::d}, "[a]{+ 0d:1$d}"}, - {"a%+0 d", {Conv::d}, "[a]{+0 d:1$d}"}, - {"a%0+ d", {Conv::d}, "[a]{0+ d:1$d}"}, - {"a% +0d", {Conv::d}, "[a]{ +0d:1$d}"}, - {"a%0 +d", {Conv::d}, "[a]{0 +d:1$d}"}, - {"a% 0+d", {Conv::d}, "[a]{ 0+d:1$d}"}, - {"a%+ 0+d", {Conv::d}, "[a]{+ 0+d:1$d}"}, + {"a%+ 0d", {FormatConversionCharSetInternal::d}, "[a]{+ 0d:1$d}"}, + {"a%+0 d", {FormatConversionCharSetInternal::d}, "[a]{+0 d:1$d}"}, + {"a%0+ d", {FormatConversionCharSetInternal::d}, "[a]{0+ d:1$d}"}, + {"a% +0d", {FormatConversionCharSetInternal::d}, "[a]{ +0d:1$d}"}, + {"a%0 +d", {FormatConversionCharSetInternal::d}, "[a]{0 +d:1$d}"}, + {"a% 0+d", {FormatConversionCharSetInternal::d}, "[a]{ 0+d:1$d}"}, + {"a%+ 0+d", {FormatConversionCharSetInternal::d}, "[a]{+ 0+d:1$d}"}, }; for (const auto& e : kExpect) { SCOPED_TRACE(e.in);
diff --git a/absl/strings/str_format_test.cc b/absl/strings/str_format_test.cc index f0d1f0a..3f14dba 100644 --- a/absl/strings/str_format_test.cc +++ b/absl/strings/str_format_test.cc
@@ -450,7 +450,7 @@ if (conv.precision.is_from_arg()) { *out += "." + std::to_string(conv.precision.get_from_arg()) + "$*"; } - *out += FormatConversionCharToChar(conv.conv); + *out += str_format_internal::FormatConversionCharToChar(conv.conv); *out += "}"; return true; } @@ -532,76 +532,103 @@ EXPECT_FALSE((ParsedFormat<'s', 'd', 'g'>::New(format))); } -using str_format_internal::Conv; +using absl::str_format_internal::FormatConversionCharSet; TEST_F(ParsedFormatTest, UncheckedCorrect) { - auto f = ExtendedParsedFormat<Conv::d>::New("ABC%dDEF"); + auto f = ExtendedParsedFormat<FormatConversionCharSet::d>::New("ABC%dDEF"); ASSERT_TRUE(f); EXPECT_EQ("[ABC]{d:1$d}[DEF]", SummarizeParsedFormat(*f)); std::string format = "%sFFF%dZZZ%f"; - auto f2 = ExtendedParsedFormat<Conv::kString, Conv::d, Conv::kFloating>::New( - format); + auto f2 = + ExtendedParsedFormat<FormatConversionCharSet::kString, + FormatConversionCharSet::d, + FormatConversionCharSet::kFloating>::New(format); ASSERT_TRUE(f2); EXPECT_EQ("{s:1$s}[FFF]{d:2$d}[ZZZ]{f:3$f}", SummarizeParsedFormat(*f2)); - f2 = ExtendedParsedFormat<Conv::kString, Conv::d, Conv::kFloating>::New( - "%s %d %f"); + f2 = + ExtendedParsedFormat<FormatConversionCharSet::kString, + FormatConversionCharSet::d, + FormatConversionCharSet::kFloating>::New("%s %d %f"); ASSERT_TRUE(f2); EXPECT_EQ("{s:1$s}[ ]{d:2$d}[ ]{f:3$f}", SummarizeParsedFormat(*f2)); - auto star = ExtendedParsedFormat<Conv::kStar, Conv::d>::New("%*d"); + auto star = ExtendedParsedFormat<FormatConversionCharSet::kStar, + FormatConversionCharSet::d>::New("%*d"); ASSERT_TRUE(star); EXPECT_EQ("{*d:2$1$*d}", SummarizeParsedFormat(*star)); - auto dollar = ExtendedParsedFormat<Conv::d, Conv::s>::New("%2$s %1$d"); + auto dollar = + ExtendedParsedFormat<FormatConversionCharSet::d, + FormatConversionCharSet::s>::New("%2$s %1$d"); ASSERT_TRUE(dollar); EXPECT_EQ("{2$s:2$s}[ ]{1$d:1$d}", SummarizeParsedFormat(*dollar)); // with reuse - dollar = ExtendedParsedFormat<Conv::d, Conv::s>::New("%2$s %1$d %1$d"); + dollar = + ExtendedParsedFormat<FormatConversionCharSet::d, + FormatConversionCharSet::s>::New("%2$s %1$d %1$d"); ASSERT_TRUE(dollar); EXPECT_EQ("{2$s:2$s}[ ]{1$d:1$d}[ ]{1$d:1$d}", SummarizeParsedFormat(*dollar)); } TEST_F(ParsedFormatTest, UncheckedIgnoredArgs) { - EXPECT_FALSE((ExtendedParsedFormat<Conv::d, Conv::s>::New("ABC"))); - EXPECT_FALSE((ExtendedParsedFormat<Conv::d, Conv::s>::New("%dABC"))); - EXPECT_FALSE((ExtendedParsedFormat<Conv::d, Conv::s>::New("ABC%2$s"))); - auto f = ExtendedParsedFormat<Conv::d, Conv::s>::NewAllowIgnored("ABC"); + EXPECT_FALSE((ExtendedParsedFormat<FormatConversionCharSet::d, + FormatConversionCharSet::s>::New("ABC"))); + EXPECT_FALSE( + (ExtendedParsedFormat<FormatConversionCharSet::d, + FormatConversionCharSet::s>::New("%dABC"))); + EXPECT_FALSE( + (ExtendedParsedFormat<FormatConversionCharSet::d, + FormatConversionCharSet::s>::New("ABC%2$s"))); + auto f = + ExtendedParsedFormat<FormatConversionCharSet::d, + FormatConversionCharSet::s>::NewAllowIgnored("ABC"); ASSERT_TRUE(f); EXPECT_EQ("[ABC]", SummarizeParsedFormat(*f)); - f = ExtendedParsedFormat<Conv::d, Conv::s>::NewAllowIgnored("%dABC"); + f = ExtendedParsedFormat< + FormatConversionCharSet::d, + FormatConversionCharSet::s>::NewAllowIgnored("%dABC"); ASSERT_TRUE(f); EXPECT_EQ("{d:1$d}[ABC]", SummarizeParsedFormat(*f)); - f = ExtendedParsedFormat<Conv::d, Conv::s>::NewAllowIgnored("ABC%2$s"); + f = ExtendedParsedFormat< + FormatConversionCharSet::d, + FormatConversionCharSet::s>::NewAllowIgnored("ABC%2$s"); ASSERT_TRUE(f); EXPECT_EQ("[ABC]{2$s:2$s}", SummarizeParsedFormat(*f)); } TEST_F(ParsedFormatTest, UncheckedMultipleTypes) { - auto dx = ExtendedParsedFormat<Conv::d | Conv::x>::New("%1$d %1$x"); + auto dx = ExtendedParsedFormat<FormatConversionCharSet::d | + FormatConversionCharSet::x>::New("%1$d %1$x"); EXPECT_TRUE(dx); EXPECT_EQ("{1$d:1$d}[ ]{1$x:1$x}", SummarizeParsedFormat(*dx)); - dx = ExtendedParsedFormat<Conv::d | Conv::x>::New("%1$d"); + dx = ExtendedParsedFormat<FormatConversionCharSet::d | + FormatConversionCharSet::x>::New("%1$d"); EXPECT_TRUE(dx); EXPECT_EQ("{1$d:1$d}", SummarizeParsedFormat(*dx)); } TEST_F(ParsedFormatTest, UncheckedIncorrect) { - EXPECT_FALSE(ExtendedParsedFormat<Conv::d>::New("")); + EXPECT_FALSE(ExtendedParsedFormat<FormatConversionCharSet::d>::New("")); - EXPECT_FALSE(ExtendedParsedFormat<Conv::d>::New("ABC%dDEF%d")); + EXPECT_FALSE( + ExtendedParsedFormat<FormatConversionCharSet::d>::New("ABC%dDEF%d")); std::string format = "%sFFF%dZZZ%f"; - EXPECT_FALSE((ExtendedParsedFormat<Conv::s, Conv::d, Conv::g>::New(format))); + EXPECT_FALSE((ExtendedParsedFormat<FormatConversionCharSet::s, + FormatConversionCharSet::d, + FormatConversionCharSet::g>::New(format))); } TEST_F(ParsedFormatTest, RegressionMixPositional) { - EXPECT_FALSE((ExtendedParsedFormat<Conv::d, Conv::o>::New("%1$d %o"))); + EXPECT_FALSE( + (ExtendedParsedFormat<FormatConversionCharSet::d, + FormatConversionCharSet::o>::New("%1$d %o"))); } using FormatWrapperTest = ::testing::Test;
diff --git a/absl/strings/substitute.h b/absl/strings/substitute.h index e7b4c1e..92c4723 100644 --- a/absl/strings/substitute.h +++ b/absl/strings/substitute.h
@@ -50,7 +50,7 @@ // // Supported types: // * absl::string_view, std::string, const char* (null is equivalent to "") -// * int32_t, int64_t, uint32_t, uint64 +// * int32_t, int64_t, uint32_t, uint64_t // * float, double // * bool (Printed as "true" or "false") // * pointer types other than char* (Printed as "0x<lower case hex string>",
diff --git a/absl/synchronization/internal/create_thread_identity.cc b/absl/synchronization/internal/create_thread_identity.cc index fa0070a..53a71b3 100644 --- a/absl/synchronization/internal/create_thread_identity.cc +++ b/absl/synchronization/internal/create_thread_identity.cc
@@ -32,9 +32,9 @@ // ThreadIdentity storage is persistent, we maintain a free-list of previously // released ThreadIdentity objects. -static base_internal::SpinLock freelist_lock( - base_internal::kLinkerInitialized); -static base_internal::ThreadIdentity* thread_identity_freelist; +ABSL_CONST_INIT static base_internal::SpinLock freelist_lock( + absl::kConstInit, base_internal::SCHEDULE_KERNEL_ONLY); +ABSL_CONST_INIT 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.
diff --git a/absl/synchronization/internal/graphcycles.cc b/absl/synchronization/internal/graphcycles.cc index 6a2bcdf..19f9aab 100644 --- a/absl/synchronization/internal/graphcycles.cc +++ b/absl/synchronization/internal/graphcycles.cc
@@ -51,9 +51,9 @@ // Avoid LowLevelAlloc's default arena since it calls malloc hooks in // which people are doing things like acquiring Mutexes. -static absl::base_internal::SpinLock arena_mu( - absl::base_internal::kLinkerInitialized); -static base_internal::LowLevelAlloc::Arena* arena; +ABSL_CONST_INIT static absl::base_internal::SpinLock arena_mu( + absl::kConstInit, base_internal::SCHEDULE_KERNEL_ONLY); +ABSL_CONST_INIT static base_internal::LowLevelAlloc::Arena* arena; static void InitArenaIfNecessary() { arena_mu.Lock();
diff --git a/absl/synchronization/mutex.cc b/absl/synchronization/mutex.cc index 8cda5a1..1f8a696 100644 --- a/absl/synchronization/mutex.cc +++ b/absl/synchronization/mutex.cc
@@ -207,12 +207,12 @@ //------------------------------------------------------------------ // Data for doing deadlock detection. -static absl::base_internal::SpinLock deadlock_graph_mu( - absl::base_internal::kLinkerInitialized); +ABSL_CONST_INIT static absl::base_internal::SpinLock deadlock_graph_mu( + absl::kConstInit, base_internal::SCHEDULE_KERNEL_ONLY); -// graph used to detect deadlocks. -static GraphCycles *deadlock_graph ABSL_GUARDED_BY(deadlock_graph_mu) - ABSL_PT_GUARDED_BY(deadlock_graph_mu); +// Graph used to detect deadlocks. +ABSL_CONST_INIT static GraphCycles *deadlock_graph + ABSL_GUARDED_BY(deadlock_graph_mu) ABSL_PT_GUARDED_BY(deadlock_graph_mu); //------------------------------------------------------------------ // An event mechanism for debugging mutex use. @@ -273,13 +273,12 @@ {0, "SignalAll on "}, }; -static absl::base_internal::SpinLock synch_event_mu( - absl::base_internal::kLinkerInitialized); -// protects synch_event +ABSL_CONST_INIT static absl::base_internal::SpinLock synch_event_mu( + absl::kConstInit, base_internal::SCHEDULE_KERNEL_ONLY); // Hash table size; should be prime > 2. // Can't be too small, as it's used for deadlock detection information. -static const uint32_t kNSynchEvent = 1031; +static constexpr uint32_t kNSynchEvent = 1031; static struct SynchEvent { // this is a trivial hash table for the events // struct is freed when refcount reaches 0
diff --git a/absl/time/clock.cc b/absl/time/clock.cc index 3b895c3..e5c423c 100644 --- a/absl/time/clock.cc +++ b/absl/time/clock.cc
@@ -226,9 +226,9 @@ // A reader-writer lock protecting the static locations below. // See SeqAcquire() and SeqRelease() above. -static absl::base_internal::SpinLock lock( - absl::base_internal::kLinkerInitialized); -static std::atomic<uint64_t> seq(0); +ABSL_CONST_INIT static absl::base_internal::SpinLock lock( + absl::kConstInit, base_internal::SCHEDULE_KERNEL_ONLY); +ABSL_CONST_INIT static std::atomic<uint64_t> seq(0); // data from a sample of the kernel's time value struct TimeSampleAtomic {
diff --git a/absl/time/internal/cctz/src/cctz_benchmark.cc b/absl/time/internal/cctz/src/cctz_benchmark.cc index d30a644..a402760 100644 --- a/absl/time/internal/cctz/src/cctz_benchmark.cc +++ b/absl/time/internal/cctz/src/cctz_benchmark.cc
@@ -280,6 +280,7 @@ "America/North_Dakota/Beulah", "America/North_Dakota/Center", "America/North_Dakota/New_Salem", + "America/Nuuk", "America/Ojinaga", "America/Panama", "America/Pangnirtung",
diff --git a/absl/time/internal/cctz/src/time_zone_lookup_test.cc b/absl/time/internal/cctz/src/time_zone_lookup_test.cc index 35911ce..0b0c1a3 100644 --- a/absl/time/internal/cctz/src/time_zone_lookup_test.cc +++ b/absl/time/internal/cctz/src/time_zone_lookup_test.cc
@@ -211,6 +211,7 @@ "America/North_Dakota/Beulah", "America/North_Dakota/Center", "America/North_Dakota/New_Salem", + "America/Nuuk", "America/Ojinaga", "America/Panama", "America/Pangnirtung",
diff --git a/absl/time/internal/cctz/testdata/version b/absl/time/internal/cctz/testdata/version index db18f83..7f680ee 100644 --- a/absl/time/internal/cctz/testdata/version +++ b/absl/time/internal/cctz/testdata/version
@@ -1 +1 @@ -2019c +2020a
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Casablanca b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Casablanca index 245f4eb..d39016b 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/Casablanca +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/Casablanca Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Africa/El_Aaiun b/absl/time/internal/cctz/testdata/zoneinfo/Africa/El_Aaiun index a91f65f..066fbed 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Africa/El_Aaiun +++ b/absl/time/internal/cctz/testdata/zoneinfo/Africa/El_Aaiun Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Dawson b/absl/time/internal/cctz/testdata/zoneinfo/America/Dawson index db9cead..2b6c3ee 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Dawson +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Dawson Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Nuuk b/absl/time/internal/cctz/testdata/zoneinfo/America/Nuuk new file mode 100644 index 0000000..0160308 --- /dev/null +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Nuuk Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/America/Whitehorse b/absl/time/internal/cctz/testdata/zoneinfo/America/Whitehorse index fb3cd71..062b58c 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/America/Whitehorse +++ b/absl/time/internal/cctz/testdata/zoneinfo/America/Whitehorse Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chongqing b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chongqing index 3c0bef2..91f6f8b 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chongqing +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chongqing Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chungking b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chungking index 3c0bef2..91f6f8b 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chungking +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Chungking Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Harbin b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Harbin index 3c0bef2..91f6f8b 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Harbin +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Harbin Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Shanghai b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Shanghai index 3c0bef2..91f6f8b 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Asia/Shanghai +++ b/absl/time/internal/cctz/testdata/zoneinfo/Asia/Shanghai Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/Canada/Yukon b/absl/time/internal/cctz/testdata/zoneinfo/Canada/Yukon index fb3cd71..062b58c 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/Canada/Yukon +++ b/absl/time/internal/cctz/testdata/zoneinfo/Canada/Yukon Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/PRC b/absl/time/internal/cctz/testdata/zoneinfo/PRC index 3c0bef2..91f6f8b 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/PRC +++ b/absl/time/internal/cctz/testdata/zoneinfo/PRC Binary files differ
diff --git a/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab b/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab index 822ffa1..53ee77e 100644 --- a/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab +++ b/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab
@@ -128,8 +128,8 @@ CA +5946-12014 America/Dawson_Creek MST - BC (Dawson Cr, Ft St John) CA +5848-12242 America/Fort_Nelson MST - BC (Ft Nelson) CA +4916-12307 America/Vancouver Pacific - BC (most areas) -CA +6043-13503 America/Whitehorse Pacific - Yukon (south) -CA +6404-13925 America/Dawson Pacific - Yukon (north) +CA +6043-13503 America/Whitehorse Pacific - Yukon (east) +CA +6404-13925 America/Dawson Pacific - Yukon (west) CC -1210+09655 Indian/Cocos CH,DE,LI +4723+00832 Europe/Zurich Swiss time CI,BF,GM,GN,ML,MR,SH,SL,SN,TG +0519-00402 Africa/Abidjan @@ -173,7 +173,7 @@ GF +0456-05220 America/Cayenne GH +0533-00013 Africa/Accra GI +3608-00521 Europe/Gibraltar -GL +6411-05144 America/Godthab Greenland (most areas) +GL +6411-05144 America/Nuuk Greenland (most areas) GL +7646-01840 America/Danmarkshavn National Park (east coast) GL +7029-02158 America/Scoresbysund Scoresbysund/Ittoqqortoormiit GL +7634-06847 America/Thule Thule/Pituffik @@ -290,7 +290,7 @@ RU +5443+02030 Europe/Kaliningrad MSK-01 - Kaliningrad RU +554521+0373704 Europe/Moscow MSK+00 - Moscow area # Mention RU and UA alphabetically. See "territorial claims" above. -RU,UA +4457+03406 Europe/Simferopol MSK+00 - Crimea +RU,UA +4457+03406 Europe/Simferopol Crimea RU +5836+04939 Europe/Kirov MSK+00 - Kirov RU +4621+04803 Europe/Astrakhan MSK+01 - Astrakhan RU +4844+04425 Europe/Volgograd MSK+01 - Volgograd @@ -341,8 +341,8 @@ TV -0831+17913 Pacific/Funafuti TW +2503+12130 Asia/Taipei UA +5026+03031 Europe/Kiev Ukraine (most areas) -UA +4837+02218 Europe/Uzhgorod Ruthenia -UA +4750+03510 Europe/Zaporozhye Zaporozh'ye/Zaporizhia; Lugansk/Luhansk (east) +UA +4837+02218 Europe/Uzhgorod Transcarpathia +UA +4750+03510 Europe/Zaporozhye Zaporozhye and east Lugansk UM +1917+16637 Pacific/Wake Wake Island US +404251-0740023 America/New_York Eastern (most areas) US +421953-0830245 America/Detroit Eastern - MI (most areas)