diff --git a/CMake/AbseilDll.cmake b/CMake/AbseilDll.cmake index 1927279..7f03025 100644 --- a/CMake/AbseilDll.cmake +++ b/CMake/AbseilDll.cmake
@@ -150,6 +150,7 @@ "hash/internal/low_level_hash.cc" "log/absl_check.h" "log/absl_log.h" + "log/absl_vlog_is_on.h" "log/check.h" "log/die_if_null.cc" "log/die_if_null.h" @@ -180,6 +181,8 @@ "log/internal/proto.cc" "log/internal/strip.h" "log/internal/structured.h" + "log/internal/vlog_config.cc" + "log/internal/vlog_config.h" "log/internal/voidify.h" "log/initialize.cc" "log/initialize.h" @@ -191,6 +194,7 @@ "log/log_sink_registry.h" "log/log_streamer.h" "log/structured.h" + "log/vlog_is_on.h" "memory/memory.h" "meta/type_traits.h" "numeric/bits.h"
diff --git a/absl/algorithm/BUILD.bazel b/absl/algorithm/BUILD.bazel index a339dc1..ddf9e11 100644 --- a/absl/algorithm/BUILD.bazel +++ b/absl/algorithm/BUILD.bazel
@@ -66,6 +66,7 @@ deps = [ ":algorithm", "//absl/base:core_headers", + "//absl/base:nullability", "//absl/meta:type_traits", ], )
diff --git a/absl/algorithm/CMakeLists.txt b/absl/algorithm/CMakeLists.txt index 181b49c..5577164 100644 --- a/absl/algorithm/CMakeLists.txt +++ b/absl/algorithm/CMakeLists.txt
@@ -50,6 +50,7 @@ absl::algorithm absl::core_headers absl::meta + absl::nullability PUBLIC )
diff --git a/absl/algorithm/container.h b/absl/algorithm/container.h index b6684c0..934dd17 100644 --- a/absl/algorithm/container.h +++ b/absl/algorithm/container.h
@@ -52,6 +52,7 @@ #include "absl/algorithm/algorithm.h" #include "absl/base/macros.h" +#include "absl/base/nullability.h" #include "absl/meta/type_traits.h" namespace absl {
diff --git a/absl/base/BUILD.bazel b/absl/base/BUILD.bazel index 60d05a8..06bd495 100644 --- a/absl/base/BUILD.bazel +++ b/absl/base/BUILD.bazel
@@ -287,6 +287,7 @@ ":cycleclock_internal", ":dynamic_annotations", ":log_severity", + ":nullability", ":raw_logging_internal", ":spinlock_wait", "//absl/meta:type_traits", @@ -549,6 +550,7 @@ ":base", ":config", ":core_headers", + ":nullability", ], )
diff --git a/absl/base/CMakeLists.txt b/absl/base/CMakeLists.txt index 9ca5cf8..4cfc228 100644 --- a/absl/base/CMakeLists.txt +++ b/absl/base/CMakeLists.txt
@@ -247,6 +247,7 @@ absl::core_headers absl::dynamic_annotations absl::log_severity + absl::nullability absl::raw_logging_internal absl::spinlock_wait absl::type_traits @@ -475,6 +476,7 @@ absl::base absl::config absl::core_headers + absl::nullability PUBLIC )
diff --git a/absl/base/call_once.h b/absl/base/call_once.h index 08436ba..7b0e69c 100644 --- a/absl/base/call_once.h +++ b/absl/base/call_once.h
@@ -37,6 +37,7 @@ #include "absl/base/internal/scheduling_mode.h" #include "absl/base/internal/spinlock_wait.h" #include "absl/base/macros.h" +#include "absl/base/nullability.h" #include "absl/base/optimization.h" #include "absl/base/port.h" @@ -46,7 +47,8 @@ class once_flag; namespace base_internal { -std::atomic<uint32_t>* ControlWord(absl::once_flag* flag); +absl::Nonnull<std::atomic<uint32_t>*> ControlWord( + absl::Nonnull<absl::once_flag*> flag); } // namespace base_internal // call_once() @@ -89,7 +91,8 @@ once_flag& operator=(const once_flag&) = delete; private: - friend std::atomic<uint32_t>* base_internal::ControlWord(once_flag* flag); + friend absl::Nonnull<std::atomic<uint32_t>*> base_internal::ControlWord( + absl::Nonnull<once_flag*> flag); std::atomic<uint32_t> control_; }; @@ -103,7 +106,8 @@ // Like call_once, but uses KERNEL_ONLY scheduling. Intended to be used to // initialize entities used by the scheduler implementation. template <typename Callable, typename... Args> -void LowLevelCallOnce(absl::once_flag* flag, Callable&& fn, Args&&... args); +void LowLevelCallOnce(absl::Nonnull<absl::once_flag*> flag, Callable&& fn, + Args&&... args); // Disables scheduling while on stack when scheduling mode is non-cooperative. // No effect for cooperative scheduling modes. @@ -143,10 +147,10 @@ }; template <typename Callable, typename... Args> -ABSL_ATTRIBUTE_NOINLINE -void CallOnceImpl(std::atomic<uint32_t>* control, - base_internal::SchedulingMode scheduling_mode, Callable&& fn, - Args&&... args) { +ABSL_ATTRIBUTE_NOINLINE void CallOnceImpl( + absl::Nonnull<std::atomic<uint32_t>*> control, + base_internal::SchedulingMode scheduling_mode, Callable&& fn, + Args&&... args) { #ifndef NDEBUG { uint32_t old_control = control->load(std::memory_order_relaxed); @@ -185,12 +189,14 @@ } // else *control is already kOnceDone } -inline std::atomic<uint32_t>* ControlWord(once_flag* flag) { +inline absl::Nonnull<std::atomic<uint32_t>*> ControlWord( + absl::Nonnull<once_flag*> flag) { return &flag->control_; } template <typename Callable, typename... Args> -void LowLevelCallOnce(absl::once_flag* flag, Callable&& fn, Args&&... args) { +void LowLevelCallOnce(absl::Nonnull<absl::once_flag*> flag, Callable&& fn, + Args&&... args) { std::atomic<uint32_t>* once = base_internal::ControlWord(flag); uint32_t s = once->load(std::memory_order_acquire); if (ABSL_PREDICT_FALSE(s != base_internal::kOnceDone)) {
diff --git a/absl/base/config.h b/absl/base/config.h index b0e7fe8..0fb6692 100644 --- a/absl/base/config.h +++ b/absl/base/config.h
@@ -609,6 +609,22 @@ #define ABSL_HAVE_STD_STRING_VIEW 1 #endif +// ABSL_HAVE_STD_ORDERING +// +// Checks whether C++20 std::{partial,weak,strong}_ordering are available. +// +// __cpp_lib_three_way_comparison is missing on libc++ +// (https://github.com/llvm/llvm-project/issues/73953) so treat it as defined +// when building in C++20 mode. +#ifdef ABSL_HAVE_STD_ORDERING +#error "ABSL_HAVE_STD_ORDERING cannot be directly set." +#elif (defined(__cpp_lib_three_way_comparison) && \ + __cpp_lib_three_way_comparison >= 201907L) || \ + (defined(ABSL_INTERNAL_CPLUSPLUS_LANG) && \ + ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L) +#define ABSL_HAVE_STD_ORDERING 1 +#endif + // ABSL_USES_STD_ANY // // Indicates whether absl::any is an alias for std::any. @@ -671,6 +687,22 @@ #error options.h is misconfigured. #endif +// ABSL_USES_STD_ORDERING +// +// Indicates whether absl::{partial,weak,strong}_ordering are aliases for the +// std:: ordering types. +#if !defined(ABSL_OPTION_USE_STD_ORDERING) +#error options.h is misconfigured. +#elif ABSL_OPTION_USE_STD_ORDERING == 0 || \ + (ABSL_OPTION_USE_STD_ORDERING == 2 && !defined(ABSL_HAVE_STD_ORDERING)) +#undef ABSL_USES_STD_ORDERING +#elif ABSL_OPTION_USE_STD_ORDERING == 1 || \ + (ABSL_OPTION_USE_STD_ORDERING == 2 && defined(ABSL_HAVE_STD_ORDERING)) +#define ABSL_USES_STD_ORDERING 1 +#else +#error options.h is misconfigured. +#endif + // In debug mode, MSVC 2017's std::variant throws a EXCEPTION_ACCESS_VIOLATION // SEH exception from emplace for variant<SomeStruct> when constructing the // struct can throw. This defeats some of variant_test and
diff --git a/absl/base/internal/endian.h b/absl/base/internal/endian.h index 50747d7..943f3d9 100644 --- a/absl/base/internal/endian.h +++ b/absl/base/internal/endian.h
@@ -22,6 +22,7 @@ #include "absl/base/casts.h" #include "absl/base/config.h" #include "absl/base/internal/unaligned_access.h" +#include "absl/base/nullability.h" #include "absl/base/port.h" namespace absl { @@ -160,27 +161,27 @@ } // Functions to do unaligned loads and stores in little-endian order. -inline uint16_t Load16(const void *p) { +inline uint16_t Load16(absl::Nonnull<const void *> p) { return ToHost16(ABSL_INTERNAL_UNALIGNED_LOAD16(p)); } -inline void Store16(void *p, uint16_t v) { +inline void Store16(absl::Nonnull<void *> p, uint16_t v) { ABSL_INTERNAL_UNALIGNED_STORE16(p, FromHost16(v)); } -inline uint32_t Load32(const void *p) { +inline uint32_t Load32(absl::Nonnull<const void *> p) { return ToHost32(ABSL_INTERNAL_UNALIGNED_LOAD32(p)); } -inline void Store32(void *p, uint32_t v) { +inline void Store32(absl::Nonnull<void *> p, uint32_t v) { ABSL_INTERNAL_UNALIGNED_STORE32(p, FromHost32(v)); } -inline uint64_t Load64(const void *p) { +inline uint64_t Load64(absl::Nonnull<const void *> p) { return ToHost64(ABSL_INTERNAL_UNALIGNED_LOAD64(p)); } -inline void Store64(void *p, uint64_t v) { +inline void Store64(absl::Nonnull<void *> p, uint64_t v) { ABSL_INTERNAL_UNALIGNED_STORE64(p, FromHost64(v)); } @@ -250,27 +251,27 @@ } // Functions to do unaligned loads and stores in big-endian order. -inline uint16_t Load16(const void *p) { +inline uint16_t Load16(absl::Nonnull<const void *> p) { return ToHost16(ABSL_INTERNAL_UNALIGNED_LOAD16(p)); } -inline void Store16(void *p, uint16_t v) { +inline void Store16(absl::Nonnull<void *> p, uint16_t v) { ABSL_INTERNAL_UNALIGNED_STORE16(p, FromHost16(v)); } -inline uint32_t Load32(const void *p) { +inline uint32_t Load32(absl::Nonnull<const void *> p) { return ToHost32(ABSL_INTERNAL_UNALIGNED_LOAD32(p)); } -inline void Store32(void *p, uint32_t v) { +inline void Store32(absl::Nonnull<void *>p, uint32_t v) { ABSL_INTERNAL_UNALIGNED_STORE32(p, FromHost32(v)); } -inline uint64_t Load64(const void *p) { +inline uint64_t Load64(absl::Nonnull<const void *> p) { return ToHost64(ABSL_INTERNAL_UNALIGNED_LOAD64(p)); } -inline void Store64(void *p, uint64_t v) { +inline void Store64(absl::Nonnull<void *> p, uint64_t v) { ABSL_INTERNAL_UNALIGNED_STORE64(p, FromHost64(v)); }
diff --git a/absl/base/internal/raw_logging.cc b/absl/base/internal/raw_logging.cc index 69b9426..d32b40a 100644 --- a/absl/base/internal/raw_logging.cc +++ b/absl/base/internal/raw_logging.cc
@@ -42,8 +42,9 @@ // This preprocessor token is also defined in raw_io.cc. If you need to copy // this, consider moving both to config.h instead. #if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \ - defined(__Fuchsia__) || defined(__native_client__) || \ - defined(__OpenBSD__) || defined(__EMSCRIPTEN__) || defined(__ASYLO__) + defined(__hexagon__) || defined(__Fuchsia__) || \ + defined(__native_client__) || defined(__OpenBSD__) || \ + defined(__EMSCRIPTEN__) || defined(__ASYLO__) #include <unistd.h>
diff --git a/absl/base/internal/unaligned_access.h b/absl/base/internal/unaligned_access.h index 093dd9b..4fea457 100644 --- a/absl/base/internal/unaligned_access.h +++ b/absl/base/internal/unaligned_access.h
@@ -23,6 +23,7 @@ #include "absl/base/attributes.h" #include "absl/base/config.h" +#include "absl/base/nullability.h" // unaligned APIs @@ -35,29 +36,35 @@ ABSL_NAMESPACE_BEGIN namespace base_internal { -inline uint16_t UnalignedLoad16(const void *p) { +inline uint16_t UnalignedLoad16(absl::Nonnull<const void *> p) { uint16_t t; memcpy(&t, p, sizeof t); return t; } -inline uint32_t UnalignedLoad32(const void *p) { +inline uint32_t UnalignedLoad32(absl::Nonnull<const void *> p) { uint32_t t; memcpy(&t, p, sizeof t); return t; } -inline uint64_t UnalignedLoad64(const void *p) { +inline uint64_t UnalignedLoad64(absl::Nonnull<const void *> p) { uint64_t t; memcpy(&t, p, sizeof t); return t; } -inline void UnalignedStore16(void *p, uint16_t v) { memcpy(p, &v, sizeof v); } +inline void UnalignedStore16(absl::Nonnull<void *> p, uint16_t v) { + memcpy(p, &v, sizeof v); +} -inline void UnalignedStore32(void *p, uint32_t v) { memcpy(p, &v, sizeof v); } +inline void UnalignedStore32(absl::Nonnull<void *> p, uint32_t v) { + memcpy(p, &v, sizeof v); +} -inline void UnalignedStore64(void *p, uint64_t v) { memcpy(p, &v, sizeof v); } +inline void UnalignedStore64(absl::Nonnull<void *> p, uint64_t v) { + memcpy(p, &v, sizeof v); +} } // namespace base_internal ABSL_NAMESPACE_END
diff --git a/absl/base/log_severity.h b/absl/base/log_severity.h index c8bcd2f..d0795a2 100644 --- a/absl/base/log_severity.h +++ b/absl/base/log_severity.h
@@ -99,13 +99,13 @@ // Returns the all-caps string representation (e.g. "INFO") of the specified // severity level if it is one of the standard levels and "UNKNOWN" otherwise. constexpr const char* LogSeverityName(absl::LogSeverity s) { - return s == absl::LogSeverity::kInfo - ? "INFO" - : s == absl::LogSeverity::kWarning - ? "WARNING" - : s == absl::LogSeverity::kError - ? "ERROR" - : s == absl::LogSeverity::kFatal ? "FATAL" : "UNKNOWN"; + switch (s) { + case absl::LogSeverity::kInfo: return "INFO"; + case absl::LogSeverity::kWarning: return "WARNING"; + case absl::LogSeverity::kError: return "ERROR"; + case absl::LogSeverity::kFatal: return "FATAL"; + default: return "UNKNOWN"; + } } // NormalizeLogSeverity() @@ -113,9 +113,10 @@ // Values less than `kInfo` normalize to `kInfo`; values greater than `kFatal` // normalize to `kError` (**NOT** `kFatal`). constexpr absl::LogSeverity NormalizeLogSeverity(absl::LogSeverity s) { - return s < absl::LogSeverity::kInfo - ? absl::LogSeverity::kInfo - : s > absl::LogSeverity::kFatal ? absl::LogSeverity::kError : s; + absl::LogSeverity n = s; + if (n < absl::LogSeverity::kInfo) n = absl::LogSeverity::kInfo; + if (n > absl::LogSeverity::kFatal) n = absl::LogSeverity::kError; + return n; } constexpr absl::LogSeverity NormalizeLogSeverity(int s) { return absl::NormalizeLogSeverity(static_cast<absl::LogSeverity>(s));
diff --git a/absl/base/options.h b/absl/base/options.h index 5c162a3..a169658 100644 --- a/absl/base/options.h +++ b/absl/base/options.h
@@ -176,6 +176,32 @@ #define ABSL_OPTION_USE_STD_VARIANT 2 +// ABSL_OPTION_USE_STD_ORDERING +// +// This option controls whether absl::{partial,weak,strong}_ordering are +// implemented as aliases to the std:: ordering types, or as an independent +// implementation. +// +// A value of 0 means to use Abseil's implementation. This requires only C++11 +// support, and is expected to work on every toolchain we support. +// +// A value of 1 means to use aliases. This requires that all code using Abseil +// is built in C++20 mode or later. +// +// A value of 2 means to detect the C++ version being used to compile Abseil, +// and use an alias only if working std:: ordering types are available. This +// option is useful when you are building your program from source. It should +// not be used otherwise -- for example, if you are distributing Abseil in a +// binary package manager -- since in mode 2, they will name different types, +// with different mangled names and binary layout, depending on the compiler +// flags passed by the end user. For more info, see +// https://abseil.io/about/design/dropin-types. +// +// User code should not inspect this macro. To check in the preprocessor if +// the ordering types are aliases of std:: ordering types, use the feature macro +// ABSL_USES_STD_ORDERING. + +#define ABSL_OPTION_USE_STD_ORDERING 2 // ABSL_OPTION_USE_INLINE_NAMESPACE // ABSL_OPTION_INLINE_NAMESPACE_NAME
diff --git a/absl/container/internal/raw_hash_set.cc b/absl/container/internal/raw_hash_set.cc index 6e5941d..6bc5f7b 100644 --- a/absl/container/internal/raw_hash_set.cc +++ b/absl/container/internal/raw_hash_set.cc
@@ -17,11 +17,13 @@ #include <atomic> #include <cassert> #include <cstddef> +#include <cstdint> #include <cstring> #include "absl/base/attributes.h" #include "absl/base/config.h" #include "absl/base/dynamic_annotations.h" +#include "absl/container/internal/container_memory.h" #include "absl/hash/hash.h" namespace absl { @@ -126,14 +128,6 @@ return find_first_non_full(common, hash); } -// Returns the address of the ith slot in slots where each slot occupies -// slot_size. -static inline void* SlotAddress(void* slot_array, size_t slot, - size_t slot_size) { - return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(slot_array) + - (slot * slot_size)); -} - // Returns the address of the slot just after slot assuming each slot has the // specified size. static inline void* NextSlot(void* slot, size_t slot_size) { @@ -254,6 +248,7 @@ c.set_size(0); if (reuse) { ResetCtrl(c, policy.slot_size); + ResetGrowthLeft(c); c.infoz().RecordStorageChanged(0, c.capacity()); } else { // We need to record infoz before calling dealloc, which will unregister @@ -268,6 +263,109 @@ } } +void HashSetResizeHelper::GrowIntoSingleGroupShuffleControlBytes( + ctrl_t* new_ctrl, size_t new_capacity) const { + assert(is_single_group(new_capacity)); + constexpr size_t kHalfWidth = Group::kWidth / 2; + assert(old_capacity_ < kHalfWidth); + + const size_t half_old_capacity = old_capacity_ / 2; + + // NOTE: operations are done with compile time known size = kHalfWidth. + // Compiler optimizes that into single ASM operation. + + // Copy second half of bytes to the beginning. + // We potentially copy more bytes in order to have compile time known size. + // Mirrored bytes from the old_ctrl_ will also be copied. + // In case of old_capacity_ == 3, we will copy 1st element twice. + // Examples: + // old_ctrl = 0S0EEEEEEE... + // new_ctrl = S0EEEEEEEE... + // + // old_ctrl = 01S01EEEEE... + // new_ctrl = 1S01EEEEEE... + // + // old_ctrl = 0123456S0123456EE... + // new_ctrl = 456S0123?????????... + std::memcpy(new_ctrl, old_ctrl_ + half_old_capacity + 1, kHalfWidth); + // Clean up copied kSentinel from old_ctrl. + new_ctrl[half_old_capacity] = ctrl_t::kEmpty; + + // Clean up damaged or uninitialized bytes. + + // Clean bytes after the intended size of the copy. + // Example: + // new_ctrl = 1E01EEEEEEE???? + // *new_ctrl= 1E0EEEEEEEE???? + // position / + std::memset(new_ctrl + old_capacity_ + 1, static_cast<int8_t>(ctrl_t::kEmpty), + kHalfWidth); + // Clean non-mirrored bytes that are not initialized. + // For small old_capacity that may be inside of mirrored bytes zone. + // Examples: + // new_ctrl = 1E0EEEEEEEE??????????.... + // *new_ctrl= 1E0EEEEEEEEEEEEE?????.... + // position / + // + // new_ctrl = 456E0123???????????... + // *new_ctrl= 456E0123EEEEEEEE???... + // position / + std::memset(new_ctrl + kHalfWidth, static_cast<int8_t>(ctrl_t::kEmpty), + kHalfWidth); + // Clean last mirrored bytes that are not initialized + // and will not be overwritten by mirroring. + // Examples: + // new_ctrl = 1E0EEEEEEEEEEEEE???????? + // *new_ctrl= 1E0EEEEEEEEEEEEEEEEEEEEE + // position S / + // + // new_ctrl = 456E0123EEEEEEEE??????????????? + // *new_ctrl= 456E0123EEEEEEEE???????EEEEEEEE + // position S / + std::memset(new_ctrl + new_capacity + kHalfWidth, + static_cast<int8_t>(ctrl_t::kEmpty), kHalfWidth); + + // Create mirrored bytes. old_capacity_ < kHalfWidth + // Example: + // new_ctrl = 456E0123EEEEEEEE???????EEEEEEEE + // *new_ctrl= 456E0123EEEEEEEE456E0123EEEEEEE + // position S/ + ctrl_t g[kHalfWidth]; + std::memcpy(g, new_ctrl, kHalfWidth); + std::memcpy(new_ctrl + new_capacity + 1, g, kHalfWidth); + + // Finally set sentinel to its place. + new_ctrl[new_capacity] = ctrl_t::kSentinel; +} + +void HashSetResizeHelper::GrowIntoSingleGroupShuffleTransferableSlots( + void* old_slots, void* new_slots, size_t slot_size) const { + assert(old_capacity_ > 0); + const size_t half_old_capacity = old_capacity_ / 2; + + SanitizerUnpoisonMemoryRegion(old_slots, slot_size * old_capacity_); + std::memcpy(new_slots, + SlotAddress(old_slots, half_old_capacity + 1, slot_size), + slot_size * half_old_capacity); + std::memcpy(SlotAddress(new_slots, half_old_capacity + 1, slot_size), + old_slots, slot_size * (half_old_capacity + 1)); +} + +void HashSetResizeHelper::GrowSizeIntoSingleGroupTransferable( + CommonFields& c, void* old_slots, size_t slot_size) { + assert(old_capacity_ < Group::kWidth / 2); + assert(is_single_group(c.capacity())); + assert(IsGrowingIntoSingleGroupApplicable(old_capacity_, c.capacity())); + + GrowIntoSingleGroupShuffleControlBytes(c.control(), c.capacity()); + GrowIntoSingleGroupShuffleTransferableSlots(old_slots, c.slot_array(), + slot_size); + + // We poison since GrowIntoSingleGroupShuffleTransferableSlots + // may leave empty slots unpoisoned. + PoisonSingleGroupEmptySlots(c, slot_size); +} + } // namespace container_internal ABSL_NAMESPACE_END } // namespace absl
diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index 067ea0d..b6d2cf9 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h
@@ -1378,6 +1378,12 @@ // `ShouldInsertBackwards()` for small tables. inline bool is_small(size_t capacity) { return capacity < Group::kWidth - 1; } +// Whether a table fits entirely into a probing group. +// Arbitrary order of elements in such tables is correct. +inline bool is_single_group(size_t capacity) { + return capacity <= Group::kWidth; +} + // Begins a probing operation on `common.control`, using `hash`. inline probe_seq<Group::kWidth> probe(const ctrl_t* ctrl, const size_t capacity, size_t hash) { @@ -1440,7 +1446,6 @@ capacity + 1 + NumClonedBytes()); ctrl[capacity] = ctrl_t::kSentinel; SanitizerPoisonMemoryRegion(common.slot_array(), slot_size * capacity); - ResetGrowthLeft(common); } // Sets `ctrl[i]` to `h`. @@ -1475,41 +1480,263 @@ return (std::max)(align_of_slot, alignof(size_t)); } -template <typename Alloc, size_t SizeOfSlot, size_t AlignOfSlot> -ABSL_ATTRIBUTE_NOINLINE void InitializeSlots(CommonFields& c, Alloc alloc) { - assert(c.capacity()); - // Folks with custom allocators often make unwarranted assumptions about the - // behavior of their classes vis-a-vis trivial destructability and what - // calls they will or won't make. Avoid sampling for people with custom - // allocators to get us out of this mess. This is not a hard guarantee but - // a workaround while we plan the exact guarantee we want to provide. - const size_t sample_size = - (std::is_same<Alloc, std::allocator<char>>::value && - c.slot_array() == nullptr) - ? SizeOfSlot - : 0; - HashtablezInfoHandle infoz = - sample_size > 0 ? Sample(sample_size) : c.infoz(); - - const bool has_infoz = infoz.IsSampled(); - const size_t cap = c.capacity(); - const size_t alloc_size = AllocSize(cap, SizeOfSlot, AlignOfSlot, has_infoz); - char* mem = static_cast<char*>( - Allocate<BackingArrayAlignment(AlignOfSlot)>(&alloc, alloc_size)); - const GenerationType old_generation = c.generation(); - c.set_generation_ptr(reinterpret_cast<GenerationType*>( - mem + GenerationOffset(cap, has_infoz))); - c.set_generation(NextGeneration(old_generation)); - c.set_control(reinterpret_cast<ctrl_t*>(mem + ControlOffset(has_infoz))); - c.set_slots(mem + SlotOffset(cap, AlignOfSlot, has_infoz)); - ResetCtrl(c, SizeOfSlot); - c.set_has_infoz(has_infoz); - if (has_infoz) { - infoz.RecordStorageChanged(c.size(), cap); - c.set_infoz(infoz); - } +// Returns the address of the ith slot in slots where each slot occupies +// slot_size. +inline void* SlotAddress(void* slot_array, size_t slot, size_t slot_size) { + return reinterpret_cast<void*>(reinterpret_cast<char*>(slot_array) + + (slot * slot_size)); } +// Helper class to perform resize of the hash set. +// +// It contains special optimizations for small group resizes. +// See GrowIntoSingleGroupShuffleControlBytes for details. +class HashSetResizeHelper { + public: + explicit HashSetResizeHelper(CommonFields& c) + : old_ctrl_(c.control()), + old_capacity_(c.capacity()), + had_infoz_(c.has_infoz()) {} + + // Optimized for small groups version of `find_first_non_full` applicable + // only right after calling `raw_hash_set::resize`. + // It has implicit assumption that `resize` will call + // `GrowSizeIntoSingleGroup*` in case `IsGrowingIntoSingleGroupApplicable`. + // Falls back to `find_first_non_full` in case of big groups, so it is + // safe to use after `rehash_and_grow_if_necessary`. + static FindInfo FindFirstNonFullAfterResize(const CommonFields& c, + size_t old_capacity, + size_t hash) { + if (!IsGrowingIntoSingleGroupApplicable(old_capacity, c.capacity())) { + return find_first_non_full(c, hash); + } + // Find a location for the new element non-deterministically. + // Note that any position is correct. + // It will located at `half_old_capacity` or one of the other + // empty slots with approximately 50% probability each. + size_t offset = probe(c, hash).offset(); + + // Note that we intentionally use unsigned int underflow. + if (offset - (old_capacity + 1) >= old_capacity) { + // Offset fall on kSentinel or into the mostly occupied first half. + offset = old_capacity / 2; + } + assert(IsEmpty(c.control()[offset])); + return FindInfo{offset, 0}; + } + + ctrl_t* old_ctrl() const { return old_ctrl_; } + size_t old_capacity() const { return old_capacity_; } + + // Allocates a backing array for the hashtable. + // Reads `capacity` and updates all other fields based on the result of + // the allocation. + // + // It also may do the folowing actions: + // 1. initialize control bytes + // 2. initialize slots + // 3. deallocate old slots. + // + // We are bundling a lot of functionality + // in one ABSL_ATTRIBUTE_NOINLINE function in order to minimize binary code + // duplication in raw_hash_set<>::resize. + // + // `c.capacity()` must be nonzero. + // POSTCONDITIONS: + // 1. CommonFields is initialized. + // + // if IsGrowingIntoSingleGroupApplicable && TransferUsesMemcpy + // Both control bytes and slots are fully initialized. + // old_slots are deallocated. + // infoz.RecordRehash is called. + // + // if IsGrowingIntoSingleGroupApplicable && !TransferUsesMemcpy + // Control bytes are fully initialized. + // infoz.RecordRehash is called. + // GrowSizeIntoSingleGroup must be called to finish slots initialization. + // + // if !IsGrowingIntoSingleGroupApplicable + // Control bytes are initialized to empty table via ResetCtrl. + // raw_hash_set<>::resize must insert elements regularly. + // infoz.RecordRehash is called if old_capacity == 0. + // + // Returns IsGrowingIntoSingleGroupApplicable result to avoid recomputation. + template <typename Alloc, size_t SizeOfSlot, bool TransferUsesMemcpy, + size_t AlignOfSlot> + ABSL_ATTRIBUTE_NOINLINE bool InitializeSlots(CommonFields& c, void* old_slots, + Alloc alloc) { + assert(c.capacity()); + // Folks with custom allocators often make unwarranted assumptions about the + // behavior of their classes vis-a-vis trivial destructability and what + // calls they will or won't make. Avoid sampling for people with custom + // allocators to get us out of this mess. This is not a hard guarantee but + // a workaround while we plan the exact guarantee we want to provide. + const size_t sample_size = + (std::is_same<Alloc, std::allocator<char>>::value && + c.slot_array() == nullptr) + ? SizeOfSlot + : 0; + HashtablezInfoHandle infoz = + sample_size > 0 ? Sample(sample_size) : c.infoz(); + + const bool has_infoz = infoz.IsSampled(); + const size_t cap = c.capacity(); + const size_t alloc_size = + AllocSize(cap, SizeOfSlot, AlignOfSlot, has_infoz); + char* mem = static_cast<char*>( + Allocate<BackingArrayAlignment(AlignOfSlot)>(&alloc, alloc_size)); + const GenerationType old_generation = c.generation(); + c.set_generation_ptr(reinterpret_cast<GenerationType*>( + mem + GenerationOffset(cap, has_infoz))); + c.set_generation(NextGeneration(old_generation)); + c.set_control(reinterpret_cast<ctrl_t*>(mem + ControlOffset(has_infoz))); + c.set_slots(mem + SlotOffset(cap, AlignOfSlot, has_infoz)); + ResetGrowthLeft(c); + + const bool grow_single_group = + IsGrowingIntoSingleGroupApplicable(old_capacity_, c.capacity()); + if (old_capacity_ != 0 && grow_single_group) { + if (TransferUsesMemcpy) { + GrowSizeIntoSingleGroupTransferable(c, old_slots, SizeOfSlot); + DeallocateOld<AlignOfSlot>(alloc, SizeOfSlot, old_slots); + } else { + GrowIntoSingleGroupShuffleControlBytes(c.control(), c.capacity()); + } + } else { + ResetCtrl(c, SizeOfSlot); + } + + c.set_has_infoz(has_infoz); + if (has_infoz) { + infoz.RecordStorageChanged(c.size(), cap); + if (grow_single_group || old_capacity_ == 0) { + infoz.RecordRehash(0); + } + c.set_infoz(infoz); + } + return grow_single_group; + } + + // Relocates slots into new single group consistent with + // GrowIntoSingleGroupShuffleControlBytes. + // + // PRECONDITIONS: + // 1. GrowIntoSingleGroupShuffleControlBytes was already called. + template <class PolicyTraits, class Alloc> + void GrowSizeIntoSingleGroup(CommonFields& c, Alloc& alloc_ref, + typename PolicyTraits::slot_type* old_slots) { + assert(old_capacity_ < Group::kWidth / 2); + assert(IsGrowingIntoSingleGroupApplicable(old_capacity_, c.capacity())); + using slot_type = typename PolicyTraits::slot_type; + assert(is_single_group(c.capacity())); + + auto* new_slots = reinterpret_cast<slot_type*>(c.slot_array()); + + size_t shuffle_bit = old_capacity_ / 2 + 1; + for (size_t i = 0; i < old_capacity_; ++i) { + if (IsFull(old_ctrl_[i])) { + size_t new_i = i ^ shuffle_bit; + SanitizerUnpoisonMemoryRegion(new_slots + new_i, sizeof(slot_type)); + PolicyTraits::transfer(&alloc_ref, new_slots + new_i, old_slots + i); + } + } + PoisonSingleGroupEmptySlots(c, sizeof(slot_type)); + } + + // Deallocates old backing array. + template <size_t AlignOfSlot, class CharAlloc> + void DeallocateOld(CharAlloc alloc_ref, size_t slot_size, void* old_slots) { + SanitizerUnpoisonMemoryRegion(old_slots, slot_size * old_capacity_); + Deallocate<BackingArrayAlignment(AlignOfSlot)>( + &alloc_ref, old_ctrl_ - ControlOffset(had_infoz_), + AllocSize(old_capacity_, slot_size, AlignOfSlot, had_infoz_)); + } + + private: + // Returns true if `GrowSizeIntoSingleGroup` can be used for resizing. + static bool IsGrowingIntoSingleGroupApplicable(size_t old_capacity, + size_t new_capacity) { + // NOTE that `old_capacity < new_capacity` in order to have + // `old_capacity < Group::kWidth / 2` to make faster copies of 8 bytes. + return is_single_group(new_capacity) && old_capacity < new_capacity; + } + + // Relocates control bytes and slots into new single group for + // transferable objects. + // Must be called only if IsGrowingIntoSingleGroupApplicable returned true. + void GrowSizeIntoSingleGroupTransferable(CommonFields& c, void* old_slots, + size_t slot_size); + + // Shuffle control bits deterministically to the next capacity. + // Returns offset for newly added element with given hash. + // + // PRECONDITIONs: + // 1. new_ctrl is allocated for new_capacity, + // but not initialized. + // 2. new_capacity is a single group. + // + // All elements are transferred into the first `old_capacity + 1` positions + // of the new_ctrl. Elements are rotated by `old_capacity_ / 2 + 1` positions + // in order to change an order and keep it non deterministic. + // Although rotation itself deterministic, position of the new added element + // will be based on `H1` and is not deterministic. + // + // Examples: + // S = kSentinel, E = kEmpty + // + // old_ctrl = SEEEEEEEE... + // new_ctrl = ESEEEEEEE... + // + // old_ctrl = 0SEEEEEEE... + // new_ctrl = E0ESE0EEE... + // + // old_ctrl = 012S012EEEEEEEEE... + // new_ctrl = 2E01EEES2E01EEE... + // + // old_ctrl = 0123456S0123456EEEEEEEEEEE... + // new_ctrl = 456E0123EEEEEES456E0123EEE... + void GrowIntoSingleGroupShuffleControlBytes(ctrl_t* new_ctrl, + size_t new_capacity) const; + + // Shuffle trivially transferable slots in the way consistent with + // GrowIntoSingleGroupShuffleControlBytes. + // + // PRECONDITIONs: + // 1. old_capacity must be non-zero. + // 2. new_ctrl is fully initialized using + // GrowIntoSingleGroupShuffleControlBytes. + // 3. new_slots is allocated and *not* poisoned. + // + // POSTCONDITIONS: + // 1. new_slots are transferred from old_slots_ consistent with + // GrowIntoSingleGroupShuffleControlBytes. + // 2. Empty new_slots are *not* poisoned. + void GrowIntoSingleGroupShuffleTransferableSlots(void* old_slots, + void* new_slots, + size_t slot_size) const; + + // Poison empty slots that were transferred using the deterministic algorithm + // described above. + // PRECONDITIONs: + // 1. new_ctrl is fully initialized using + // GrowIntoSingleGroupShuffleControlBytes. + // 2. new_slots is fully initialized consistent with + // GrowIntoSingleGroupShuffleControlBytes. + void PoisonSingleGroupEmptySlots(CommonFields& c, size_t slot_size) const { + // poison non full items + for (size_t i = 0; i < c.capacity(); ++i) { + if (!IsFull(c.control()[i])) { + SanitizerPoisonMemoryRegion(SlotAddress(c.slot_array(), i, slot_size), + slot_size); + } + } + } + + ctrl_t* old_ctrl_; + size_t old_capacity_; + bool had_infoz_; +}; + // PolicyFunctions bundles together some information for a particular // raw_hash_set<T, ...> instantiation. This information is passed to // type-erased functions that want to do small amounts of type-specific @@ -1627,6 +1854,11 @@ using AllocTraits = absl::allocator_traits<allocator_type>; using SlotAlloc = typename absl::allocator_traits< allocator_type>::template rebind_alloc<slot_type>; + // People are often sloppy with the exact type of their allocator (sometimes + // it has an extra const or is missing the pair, but rebinds made it work + // anyway). + using CharAlloc = + typename absl::allocator_traits<Alloc>::template rebind_alloc<char>; using SlotAllocTraits = typename absl::allocator_traits< allocator_type>::template rebind_traits<slot_type>; @@ -1819,8 +2051,7 @@ const allocator_type& alloc = allocator_type()) : settings_(CommonFields{}, hash, eq, alloc) { if (bucket_count) { - common().set_capacity(NormalizeCapacity(bucket_count)); - initialize_slots(); + resize(NormalizeCapacity(bucket_count)); } } @@ -2616,52 +2847,63 @@ EraseMetaOnly(common(), it.control(), sizeof(slot_type)); } - // Allocates a backing array for `self` and initializes its control bytes. - // This reads `capacity` and updates all other fields based on the result of - // the allocation. + // Resizes table to the new capacity and move all elements to the new + // positions accordingly. // - // This does not free the currently held array; `capacity` must be nonzero. - inline void initialize_slots() { - // People are often sloppy with the exact type of their allocator (sometimes - // it has an extra const or is missing the pair, but rebinds made it work - // anyway). - using CharAlloc = - typename absl::allocator_traits<Alloc>::template rebind_alloc<char>; - InitializeSlots<CharAlloc, sizeof(slot_type), alignof(slot_type)>( - common(), CharAlloc(alloc_ref())); - } - + // Note that for better performance instead of + // find_first_non_full(common(), hash), + // HashSetResizeHelper::FindFirstNonFullAfterResize( + // common(), old_capacity, hash) + // can be called right after `resize`. ABSL_ATTRIBUTE_NOINLINE void resize(size_t new_capacity) { assert(IsValidCapacity(new_capacity)); - auto* old_ctrl = control(); + HashSetResizeHelper resize_helper(common()); auto* old_slots = slot_array(); - const bool had_infoz = common().has_infoz(); - const size_t old_capacity = common().capacity(); common().set_capacity(new_capacity); - initialize_slots(); + // Note that `InitializeSlots` does different number initialization steps + // depending on the values of `transfer_uses_memcpy` and capacities. + // Refer to the comment in `InitializeSlots` for more details. + const bool grow_single_group = + resize_helper.InitializeSlots<CharAlloc, sizeof(slot_type), + PolicyTraits::transfer_uses_memcpy(), + alignof(slot_type)>( + common(), const_cast<std::remove_const_t<slot_type>*>(old_slots), + CharAlloc(alloc_ref())); - auto* new_slots = slot_array(); - size_t total_probe_length = 0; - for (size_t i = 0; i != old_capacity; ++i) { - if (IsFull(old_ctrl[i])) { - size_t hash = PolicyTraits::apply(HashElement{hash_ref()}, - PolicyTraits::element(old_slots + i)); - auto target = find_first_non_full(common(), hash); - size_t new_i = target.offset; - total_probe_length += target.probe_length; - SetCtrl(common(), new_i, H2(hash), sizeof(slot_type)); - transfer(new_slots + new_i, old_slots + i); + if (resize_helper.old_capacity() == 0) { + // InitializeSlots did all the work including infoz().RecordRehash(). + return; + } + + if (grow_single_group) { + if (PolicyTraits::transfer_uses_memcpy()) { + // InitializeSlots did all the work. + return; } + // We want GrowSizeIntoSingleGroup to be called here in order to make + // InitializeSlots not depend on PolicyTraits. + resize_helper.GrowSizeIntoSingleGroup<PolicyTraits>(common(), alloc_ref(), + old_slots); + } else { + // InitializeSlots prepares control bytes to correspond to empty table. + auto* new_slots = slot_array(); + size_t total_probe_length = 0; + for (size_t i = 0; i != resize_helper.old_capacity(); ++i) { + if (IsFull(resize_helper.old_ctrl()[i])) { + size_t hash = PolicyTraits::apply( + HashElement{hash_ref()}, PolicyTraits::element(old_slots + i)); + auto target = find_first_non_full(common(), hash); + size_t new_i = target.offset; + total_probe_length += target.probe_length; + SetCtrl(common(), new_i, H2(hash), sizeof(slot_type)); + transfer(new_slots + new_i, old_slots + i); + } + } + infoz().RecordRehash(total_probe_length); } - if (old_capacity) { - SanitizerUnpoisonMemoryRegion(old_slots, - sizeof(slot_type) * old_capacity); - Deallocate<BackingArrayAlignment(alignof(slot_type))>( - &alloc_ref(), old_ctrl - ControlOffset(had_infoz), - AllocSize(old_capacity, sizeof(slot_type), alignof(slot_type), - had_infoz)); - } - infoz().RecordRehash(total_probe_length); + resize_helper.DeallocateOld<alignof(slot_type)>( + CharAlloc(alloc_ref()), sizeof(slot_type), + const_cast<std::remove_const_t<slot_type>*>(old_slots)); } // Prunes control bytes to remove as many tombstones as possible. @@ -2830,8 +3072,17 @@ if (!rehash_for_bug_detection && ABSL_PREDICT_FALSE(growth_left() == 0 && !IsDeleted(control()[target.offset]))) { + size_t old_capacity = capacity(); rehash_and_grow_if_necessary(); - target = find_first_non_full(common(), hash); + // NOTE: It is safe to use `FindFirstNonFullAfterResize`. + // `FindFirstNonFullAfterResize` must be called right after resize. + // `rehash_and_grow_if_necessary` may *not* call `resize` + // and perform `drop_deletes_without_resize` instead. But this + // could happen only on big tables. + // For big tables `FindFirstNonFullAfterResize` will always + // fallback to normal `find_first_non_full`, so it is safe to use it. + target = HashSetResizeHelper::FindFirstNonFullAfterResize( + common(), old_capacity, hash); } common().increment_size(); set_growth_left(growth_left() - IsEmpty(control()[target.offset]));
diff --git a/absl/container/internal/raw_hash_set_test.cc b/absl/container/internal/raw_hash_set_test.cc index 8577272..756b9df 100644 --- a/absl/container/internal/raw_hash_set_test.cc +++ b/absl/container/internal/raw_hash_set_test.cc
@@ -30,6 +30,7 @@ #include <ostream> #include <random> #include <string> +#include <tuple> #include <type_traits> #include <unordered_map> #include <unordered_set> @@ -299,7 +300,7 @@ } } -template <class T> +template <class T, bool kTransferable = false> struct ValuePolicy { using slot_type = T; using key_type = T; @@ -317,10 +318,11 @@ } template <class Allocator> - static void transfer(Allocator* alloc, slot_type* new_slot, - slot_type* old_slot) { + static std::integral_constant<bool, kTransferable> transfer( + Allocator* alloc, slot_type* new_slot, slot_type* old_slot) { construct(alloc, new_slot, std::move(*old_slot)); destroy(alloc, old_slot); + return {}; } static T& element(slot_type* slot) { return *slot; } @@ -337,6 +339,8 @@ using IntPolicy = ValuePolicy<int64_t>; using Uint8Policy = ValuePolicy<uint8_t>; +using TranferableIntPolicy = ValuePolicy<int64_t, /*kTransferable=*/true>; + class StringPolicy { template <class F, class K, class V, class = typename std::enable_if< @@ -409,9 +413,10 @@ using Base::Base; }; -template <typename T> -struct ValueTable : raw_hash_set<ValuePolicy<T>, hash_default_hash<T>, - std::equal_to<T>, std::allocator<T>> { +template <typename T, bool kTransferable = false> +struct ValueTable + : raw_hash_set<ValuePolicy<T, kTransferable>, hash_default_hash<T>, + std::equal_to<T>, std::allocator<T>> { using Base = typename ValueTable::raw_hash_set; using Base::Base; }; @@ -419,6 +424,8 @@ using IntTable = ValueTable<int64_t>; using Uint8Table = ValueTable<uint8_t>; +using TransferableIntTable = ValueTable<int64_t, /*kTransferable=*/true>; + template <typename T> struct CustomAlloc : std::allocator<T> { CustomAlloc() = default; @@ -653,6 +660,68 @@ EXPECT_THAT(addr(0), original_addr_0); } +template <class TableType> +class SmallTableResizeTest : public testing::Test {}; + +TYPED_TEST_SUITE_P(SmallTableResizeTest); + +TYPED_TEST_P(SmallTableResizeTest, InsertIntoSmallTable) { + TypeParam t; + for (int i = 0; i < 32; ++i) { + t.insert(i); + ASSERT_EQ(t.size(), i + 1); + for (int j = 0; j < i + 1; ++j) { + EXPECT_TRUE(t.find(j) != t.end()); + EXPECT_EQ(*t.find(j), j); + } + } +} + +TYPED_TEST_P(SmallTableResizeTest, ResizeGrowSmallTables) { + TypeParam t; + for (size_t source_size = 0; source_size < 32; ++source_size) { + for (size_t target_size = source_size; target_size < 32; ++target_size) { + for (bool rehash : {false, true}) { + for (size_t i = 0; i < source_size; ++i) { + t.insert(static_cast<int>(i)); + } + if (rehash) { + t.rehash(target_size); + } else { + t.reserve(target_size); + } + for (size_t i = 0; i < source_size; ++i) { + EXPECT_TRUE(t.find(static_cast<int>(i)) != t.end()); + EXPECT_EQ(*t.find(static_cast<int>(i)), static_cast<int>(i)); + } + } + } + } +} + +TYPED_TEST_P(SmallTableResizeTest, ResizeReduceSmallTables) { + TypeParam t; + for (size_t source_size = 0; source_size < 32; ++source_size) { + for (size_t target_size = 0; target_size <= source_size; ++target_size) { + size_t inserted_count = std::min<size_t>(source_size, 5); + for (size_t i = 0; i < inserted_count; ++i) { + t.insert(static_cast<int>(i)); + } + t.rehash(target_size); + for (size_t i = 0; i < inserted_count; ++i) { + EXPECT_TRUE(t.find(static_cast<int>(i)) != t.end()); + EXPECT_EQ(*t.find(static_cast<int>(i)), static_cast<int>(i)); + } + } + } +} + +REGISTER_TYPED_TEST_SUITE_P(SmallTableResizeTest, InsertIntoSmallTable, + ResizeGrowSmallTables, ResizeReduceSmallTables); +using SmallTableTypes = ::testing::Types<IntTable, TransferableIntTable>; +INSTANTIATE_TYPED_TEST_SUITE_P(InstanceSmallTableResizeTest, + SmallTableResizeTest, SmallTableTypes); + TEST(Table, LazyEmplace) { StringTable t; bool called = false; @@ -1071,7 +1140,7 @@ TEST(Table, EraseMaintainsValidIterator) { IntTable t; const int kNumElements = 100; - for (int i = 0; i < kNumElements; i ++) { + for (int i = 0; i < kNumElements; i++) { EXPECT_TRUE(t.emplace(i).second); } EXPECT_EQ(t.size(), kNumElements); @@ -2258,21 +2327,34 @@ } #ifdef ABSL_HAVE_ADDRESS_SANITIZER -TEST(Sanitizer, PoisoningUnused) { - IntTable t; - t.reserve(5); - // Insert something to force an allocation. - int64_t& v1 = *t.insert(0).first; +template <class TableType> +class SanitizerTest : public testing::Test {}; - // Make sure there is something to test. - ASSERT_GT(t.capacity(), 1); +TYPED_TEST_SUITE_P(SanitizerTest); - int64_t* slots = RawHashSetTestOnlyAccess::GetSlots(t); - for (size_t i = 0; i < t.capacity(); ++i) { - EXPECT_EQ(slots + i != &v1, __asan_address_is_poisoned(slots + i)); +TYPED_TEST_P(SanitizerTest, PoisoningUnused) { + TypeParam t; + for (size_t reserve_size = 2; reserve_size < 1024; + reserve_size = reserve_size * 3 / 2) { + t.reserve(reserve_size); + // Insert something to force an allocation. + int64_t& v = *t.insert(0).first; + + // Make sure there is something to test. + ASSERT_GT(t.capacity(), 1); + + int64_t* slots = RawHashSetTestOnlyAccess::GetSlots(t); + for (size_t i = 0; i < t.capacity(); ++i) { + EXPECT_EQ(slots + i != &v, __asan_address_is_poisoned(slots + i)) << i; + } } } +REGISTER_TYPED_TEST_SUITE_P(SanitizerTest, PoisoningUnused); +using SanitizerTableTypes = ::testing::Types<IntTable, TransferableIntTable>; +INSTANTIATE_TYPED_TEST_SUITE_P(InstanceSanitizerTest, SanitizerTest, + SanitizerTableTypes); + TEST(Sanitizer, PoisoningOnErase) { IntTable t; int64_t& v = *t.insert(0).first;
diff --git a/absl/hash/hash_benchmark.cc b/absl/hash/hash_benchmark.cc index 916fb62..d18ea69 100644 --- a/absl/hash/hash_benchmark.cc +++ b/absl/hash/hash_benchmark.cc
@@ -85,12 +85,6 @@ } }; -template <typename FuncType> -inline FuncType* ODRUseFunction(FuncType* ptr) { - volatile FuncType* dummy = ptr; - return dummy; -} - absl::Cord FlatCord(size_t size) { absl::Cord result(std::string(size, 'a')); result.Flatten(); @@ -166,7 +160,7 @@ return hash<decltype(__VA_ARGS__)>{}(arg); \ } \ bool absl_hash_test_odr_use##hash##name = \ - ODRUseFunction(&Codegen##hash##name); + (benchmark::DoNotOptimize(&Codegen##hash##name), false); MAKE_BENCHMARK(AbslHash, Int32, int32_t{}); MAKE_BENCHMARK(AbslHash, Int64, int64_t{});
diff --git a/absl/log/BUILD.bazel b/absl/log/BUILD.bazel index 40c1bfb..af23bb8 100644 --- a/absl/log/BUILD.bazel +++ b/absl/log/BUILD.bazel
@@ -98,6 +98,7 @@ "//absl/flags:marshalling", "//absl/log/internal:config", "//absl/log/internal:flags", + "//absl/log/internal:vlog_config", "//absl/strings", ], # Binaries which do not access these flags from C++ still want this library linked in. @@ -143,6 +144,7 @@ copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ + ":vlog_is_on", "//absl/log/internal:log_impl", ], ) @@ -235,6 +237,57 @@ ], ) +cc_library( + name = "absl_vlog_is_on", + hdrs = ["absl_vlog_is_on.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = [ + "//absl/log:__subpackages__", + ], + deps = [ + "//absl/base:config", + "//absl/base:core_headers", + "//absl/log/internal:vlog_config", + "//absl/strings", + ], +) + +cc_library( + name = "vlog_is_on", + hdrs = ["vlog_is_on.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = [ + "//absl/log:__subpackages__", + ], + deps = [ + ":absl_vlog_is_on", + ], +) + +# TODO(b/200695798): run this in TAP projects with -DABSL_MAX_VLOG_VERBOSITY={-100,100} +cc_test( + name = "vlog_is_on_test", + size = "small", + srcs = [ + "vlog_is_on_test.cc", + ], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":flags", + ":log", + ":scoped_mock_log", + ":vlog_is_on", + "//absl/base:log_severity", + "//absl/flags:flag", + "//absl/types:optional", + "@com_google_googletest//:gtest", + "@com_google_googletest//:gtest_main", + ], +) + # Test targets cc_test( @@ -571,6 +624,7 @@ "//absl/base:strerror", "//absl/flags:program_name", "//absl/log/internal:test_helpers", + "//absl/status", "//absl/strings", "//absl/strings:str_format", "@com_google_googletest//:gtest",
diff --git a/absl/log/CMakeLists.txt b/absl/log/CMakeLists.txt index aa00886..366d7be 100644 --- a/absl/log/CMakeLists.txt +++ b/absl/log/CMakeLists.txt
@@ -157,6 +157,7 @@ absl::log_internal_conditions absl::log_internal_message absl::log_internal_strip + absl::absl_vlog_is_on ) absl_cc_library( @@ -481,6 +482,7 @@ absl::flags absl::flags_marshalling absl::strings + absl::vlog_config_internal PUBLIC ) @@ -536,6 +538,7 @@ ${ABSL_DEFAULT_LINKOPTS} DEPS absl::log_internal_log_impl + absl::vlog_is_on PUBLIC ) @@ -673,19 +676,91 @@ ) absl_cc_library( - NAME - log_internal_fnmatch - SRCS - "internal/fnmatch.cc" - HDRS - "internal/fnmatch.h" - COPTS - ${ABSL_DEFAULT_COPTS} - LINKOPTS - ${ABSL_DEFAULT_LINKOPTS} - DEPS - absl::config - absl::strings + NAME + vlog_config_internal + SRCS + "internal/vlog_config.cc" + HDRS + "internal/vlog_config.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::base + absl::config + absl::core_headers + absl::log_internal_fnmatch + absl::memory + absl::no_destructor + absl::strings + absl::synchronization + absl::optional +) + +absl_cc_library( + NAME + absl_vlog_is_on + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + HDRS + "absl_vlog_is_on.h" + DEPS + absl::vlog_config_internal + absl::config + absl::core_headers + absl::strings +) + +absl_cc_library( + NAME + vlog_is_on + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + HDRS + "vlog_is_on.h" + DEPS + absl::absl_vlog_is_on +) + +absl_cc_test( + NAME + vlog_is_on_test + SRCS + "vlog_is_on_test.cc" + COPTS + ${ABSL_TEST_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::log_flags + absl::log + absl::scoped_mock_log + absl::vlog_is_on + absl::log_severity + absl::flags + absl::optional + GTest::gmock_main +) + +absl_cc_library( + NAME + log_internal_fnmatch + SRCS + "internal/fnmatch.cc" + HDRS + "internal/fnmatch.h" + COPTS + ${ABSL_DEFAULT_COPTS} + LINKOPTS + ${ABSL_DEFAULT_LINKOPTS} + DEPS + absl::config + absl::strings ) # Test targets @@ -1021,6 +1096,7 @@ absl::log absl::log_internal_test_helpers absl::log_severity + absl::status absl::strerror absl::strings absl::str_format
diff --git a/absl/log/absl_log.h b/absl/log/absl_log.h index 0517760..0fd9ae3 100644 --- a/absl/log/absl_log.h +++ b/absl/log/absl_log.h
@@ -39,6 +39,9 @@ #define ABSL_PLOG(severity) ABSL_LOG_INTERNAL_PLOG_IMPL(_##severity) #define ABSL_DLOG(severity) ABSL_LOG_INTERNAL_DLOG_IMPL(_##severity) +#define ABSL_VLOG(verbose_level) ABSL_LOG_INTERNAL_VLOG_IMPL(verbose_level) +#define ABSL_DVLOG(verbose_level) ABSL_LOG_INTERNAL_DVLOG_IMPL(verbose_level) + #define ABSL_LOG_IF(severity, condition) \ ABSL_LOG_INTERNAL_LOG_IF_IMPL(_##severity, condition) #define ABSL_PLOG_IF(severity, condition) \ @@ -73,6 +76,15 @@ #define ABSL_DLOG_EVERY_N_SEC(severity, n_seconds) \ ABSL_LOG_INTERNAL_DLOG_EVERY_N_SEC_IMPL(_##severity, n_seconds) +#define ABSL_VLOG_EVERY_N(verbose_level, n) \ + ABSL_LOG_INTERNAL_VLOG_EVERY_N_IMPL(verbose_level, n) +#define ABSL_VLOG_FIRST_N(verbose_level, n) \ + ABSL_LOG_INTERNAL_VLOG_FIRST_N_IMPL(verbose_level, n) +#define ABSL_VLOG_EVERY_POW_2(verbose_level, n) \ + ABSL_LOG_INTERNAL_VLOG_EVERY_POW_2_IMPL(verbose_level, n) +#define ABSL_VLOG_EVERY_N_SEC(verbose_level, n) \ + ABSL_LOG_INTERNAL_VLOG_EVERY_N_SEC_IMPL(verbose_level, n) + #define ABSL_LOG_IF_EVERY_N(severity, condition, n) \ ABSL_LOG_INTERNAL_LOG_IF_EVERY_N_IMPL(_##severity, condition, n) #define ABSL_LOG_IF_FIRST_N(severity, condition, n) \
diff --git a/absl/log/absl_vlog_is_on.h b/absl/log/absl_vlog_is_on.h new file mode 100644 index 0000000..d1df2c0 --- /dev/null +++ b/absl/log/absl_vlog_is_on.h
@@ -0,0 +1,115 @@ +// Copyright 2022 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: log/absl_vlog_is_on.h +// ----------------------------------------------------------------------------- +// +// This header defines the `ABSL_VLOG_IS_ON()` macro that controls the +// variable-verbosity conditional logging. +// +// It's used by `VLOG` in log.h, or it can also be used directly like this: +// +// if (ABSL_VLOG_IS_ON(2)) { +// foo_server.RecomputeStatisticsExpensive(); +// LOG(INFO) << foo_server.LastStatisticsAsString(); +// } +// +// Each source file has an effective verbosity level that's a non-negative +// integer computed from the `--vmodule` and `--v` flags. +// `ABSL_VLOG_IS_ON(n)` is true, and `VLOG(n)` logs, if that effective verbosity +// level is greater than or equal to `n`. +// +// `--vmodule` takes a comma-delimited list of key=value pairs. Each key is a +// pattern matched against filenames, and the values give the effective severity +// level applied to matching files. '?' and '*' characters in patterns are +// interpreted as single-character and zero-or-more-character wildcards. +// Patterns including a slash character are matched against full pathnames, +// while those without are matched against basenames only. One suffix (i.e. the +// last . and everything after it) is stripped from each filename prior to +// matching, as is the special suffix "-inl". +// +// Files are matched against globs in `--vmodule` in order, and the first match +// determines the verbosity level. +// +// Files which do not match any pattern in `--vmodule` use the value of `--v` as +// their effective verbosity level. The default is 0. +// +// SetVLOGLevel helper function is provided to do limited dynamic control over +// V-logging by appending to `--vmodule`. Because these go at the beginning of +// the list, they take priority over any globs previously added. +// +// Resetting --vmodule will override all previous modifications to `--vmodule`, +// including via SetVLOGLevel. + +#ifndef ABSL_LOG_ABSL_VLOG_IS_ON_H_ +#define ABSL_LOG_ABSL_VLOG_IS_ON_H_ + +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "absl/log/internal/vlog_config.h" // IWYU pragma: export +#include "absl/strings/string_view.h" + +// IWYU pragma: private, include "absl/log/log.h" + +// This is expanded at the callsite to allow the compiler to optimize +// always-false cases out of the build. +// An ABSL_MAX_VLOG_VERBOSITY of 2 means that VLOG(3) and above should never +// log. +#ifdef ABSL_MAX_VLOG_VERBOSITY +#define ABSL_LOG_INTERNAL_MAX_LOG_VERBOSITY_CHECK(x) \ + ((x) <= ABSL_MAX_VLOG_VERBOSITY)&& +#else +#define ABSL_LOG_INTERNAL_MAX_LOG_VERBOSITY_CHECK(x) +#endif + +// Each ABSL_VLOG_IS_ON call site gets its own VLogSite that registers with the +// global linked list of sites to asynchronously update its verbosity level on +// changes to --v or --vmodule. The verbosity can also be set by manually +// calling SetVLOGLevel. +// +// ABSL_VLOG_IS_ON is not async signal safe, but it is guaranteed not to +// allocate new memory. +#define ABSL_VLOG_IS_ON(verbose_level) \ + (ABSL_LOG_INTERNAL_MAX_LOG_VERBOSITY_CHECK(verbose_level)[]() \ + ->::absl::log_internal::VLogSite * \ + { \ + ABSL_CONST_INIT static ::absl::log_internal::VLogSite site(__FILE__); \ + return &site; \ + }() \ + ->IsEnabled(verbose_level)) + +namespace absl { +ABSL_NAMESPACE_BEGIN + +// Sets the global `(ABSL_)VLOG(_IS_ON)` level to `log_level`. This level is +// applied to any sites whose filename doesn't match any `module_pattern`. +// Returns the prior value. +inline int SetGlobalVLogLevel(int log_level) { + return absl::log_internal::UpdateGlobalVLogLevel(log_level); +} + +// Sets `(ABSL_)VLOG(_IS_ON)` level for `module_pattern` to `log_level`. +// This lets us dynamically control what is normally set by the --vmodule flag. +// Returns the level that previously applied to module_pattern. +// Calling this with `log_level` of kUseFlag will have all sites for that +// pattern use the value of --v. +inline int SetVLogLevel(absl::string_view module_pattern, int log_level) { + return absl::log_internal::PrependVModule(module_pattern, log_level); +} + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_LOG_ABSL_VLOG_IS_ON_H_
diff --git a/absl/log/flags.cc b/absl/log/flags.cc index 215b7bd..287b3e9 100644 --- a/absl/log/flags.cc +++ b/absl/log/flags.cc
@@ -28,6 +28,7 @@ #include "absl/flags/marshalling.h" #include "absl/log/globals.h" #include "absl/log/internal/config.h" +#include "absl/log/internal/vlog_config.h" #include "absl/strings/numbers.h" #include "absl/strings/string_view.h" @@ -118,3 +119,25 @@ .OnUpdate([] { absl::log_internal::RawEnableLogPrefix(absl::GetFlag(FLAGS_log_prefix)); }); + +ABSL_FLAG(int, v, 0, + "Show all VLOG(m) messages for m <= this. Overridable by --vmodule.") + .OnUpdate([] { + absl::log_internal::UpdateGlobalVLogLevel(absl::GetFlag(FLAGS_v)); + }); + +ABSL_FLAG( + std::string, vmodule, "", + "per-module log verbosity level." + " Argument is a comma-separated list of <module name>=<log level>." + " <module name> is a glob pattern, matched against the filename base" + " (that is, name ignoring .cc/.h./-inl.h)." + " A pattern without slashes matches just the file name portion, otherwise" + " the whole file path below the workspace root" + " (still without .cc/.h./-inl.h) is matched." + " ? and * in the glob pattern match any single or sequence of characters" + " respectively including slashes." + " <log level> overrides any value given by --v.") + .OnUpdate([] { + absl::log_internal::UpdateVModule(absl::GetFlag(FLAGS_vmodule)); + });
diff --git a/absl/log/internal/BUILD.bazel b/absl/log/internal/BUILD.bazel index 2030a3d..1be1349 100644 --- a/absl/log/internal/BUILD.bazel +++ b/absl/log/internal/BUILD.bazel
@@ -153,6 +153,7 @@ ":conditions", ":log_message", ":strip", + "//absl/log:absl_vlog_is_on", ], ) @@ -377,6 +378,48 @@ ], ) +cc_library( + name = "vlog_config", + srcs = ["vlog_config.cc"], + hdrs = ["vlog_config.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = ["//absl/log:__subpackages__"], + deps = [ + "//absl/base", + "//absl/base:config", + "//absl/base:core_headers", + "//absl/base:no_destructor", + "//absl/log/internal:fnmatch", + "//absl/memory", + "//absl/strings", + "//absl/synchronization", + "//absl/types:optional", + ], +) + +cc_binary( + name = "vlog_config_benchmark", + testonly = 1, + srcs = ["vlog_config_benchmark.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + tags = [ + "benchmark", + ], + visibility = ["//visibility:private"], + deps = [ + ":vlog_config", + "//absl/base:config", + "//absl/base:core_headers", + "//absl/container:layout", + "//absl/memory", + "//absl/random:distributions", + "//absl/strings", + "@com_github_google_benchmark//:benchmark_main", + ], +) + # Test targets cc_test( name = "stderr_log_sink_test",
diff --git a/absl/log/internal/check_op.h b/absl/log/internal/check_op.h index 20b01b5..11f0f40 100644 --- a/absl/log/internal/check_op.h +++ b/absl/log/internal/check_op.h
@@ -65,6 +65,7 @@ ::absl::log_internal::GetReferenceableValue(val2), \ ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(val1_text \ " " #op " " val2_text))) \ + ABSL_LOG_INTERNAL_CONDITION_FATAL(STATELESS, true) \ ABSL_LOG_INTERNAL_CHECK(*absl_log_internal_check_op_result).InternalStream() #define ABSL_LOG_INTERNAL_QCHECK_OP(name, op, val1, val1_text, val2, \ val2_text) \ @@ -74,6 +75,7 @@ ::absl::log_internal::GetReferenceableValue(val2), \ ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL( \ val1_text " " #op " " val2_text))) \ + ABSL_LOG_INTERNAL_CONDITION_QFATAL(STATELESS, true) \ ABSL_LOG_INTERNAL_QCHECK(*absl_log_internal_qcheck_op_result).InternalStream() #define ABSL_LOG_INTERNAL_CHECK_STROP(func, op, expected, s1, s1_text, s2, \ s2_text) \ @@ -82,6 +84,7 @@ (s1), (s2), \ ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(s1_text " " #op \ " " s2_text))) \ + ABSL_LOG_INTERNAL_CONDITION_FATAL(STATELESS, true) \ ABSL_LOG_INTERNAL_CHECK(*absl_log_internal_check_strop_result) \ .InternalStream() #define ABSL_LOG_INTERNAL_QCHECK_STROP(func, op, expected, s1, s1_text, s2, \ @@ -91,6 +94,7 @@ (s1), (s2), \ ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(s1_text " " #op \ " " s2_text))) \ + ABSL_LOG_INTERNAL_CONDITION_QFATAL(STATELESS, true) \ ABSL_LOG_INTERNAL_QCHECK(*absl_log_internal_qcheck_strop_result) \ .InternalStream() // This one is tricky: @@ -113,6 +117,10 @@ // * As usual, no braces so we can stream into the expansion with `operator<<`. // * Also as usual, it must expand to a single (partial) statement with no // ambiguous-else problems. +// * When stripped by `ABSL_MIN_LOG_LEVEL`, we must discard the `<expr> is OK` +// string literal and abort without doing any streaming. We don't need to +// strip the call to stringify the non-ok `Status` as long as we don't log it; +// dropping the `Status`'s message text is out of scope. #define ABSL_LOG_INTERNAL_CHECK_OK(val, val_text) \ for (::std::pair<const ::absl::Status*, ::std::string*> \ absl_log_internal_check_ok_goo; \ @@ -126,22 +134,24 @@ ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(val_text \ " is OK")), \ !ABSL_PREDICT_TRUE(absl_log_internal_check_ok_goo.first->ok());) \ + ABSL_LOG_INTERNAL_CONDITION_FATAL(STATELESS, true) \ ABSL_LOG_INTERNAL_CHECK(*absl_log_internal_check_ok_goo.second) \ .InternalStream() -#define ABSL_LOG_INTERNAL_QCHECK_OK(val, val_text) \ - for (::std::pair<const ::absl::Status*, ::std::string*> \ - absl_log_internal_check_ok_goo; \ - absl_log_internal_check_ok_goo.first = \ - ::absl::log_internal::AsStatus(val), \ - absl_log_internal_check_ok_goo.second = \ - ABSL_PREDICT_TRUE(absl_log_internal_check_ok_goo.first->ok()) \ - ? nullptr \ - : ::absl::status_internal::MakeCheckFailString( \ - absl_log_internal_check_ok_goo.first, \ - ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(val_text \ - " is OK")), \ - !ABSL_PREDICT_TRUE(absl_log_internal_check_ok_goo.first->ok());) \ - ABSL_LOG_INTERNAL_QCHECK(*absl_log_internal_check_ok_goo.second) \ +#define ABSL_LOG_INTERNAL_QCHECK_OK(val, val_text) \ + for (::std::pair<const ::absl::Status*, ::std::string*> \ + absl_log_internal_qcheck_ok_goo; \ + absl_log_internal_qcheck_ok_goo.first = \ + ::absl::log_internal::AsStatus(val), \ + absl_log_internal_qcheck_ok_goo.second = \ + ABSL_PREDICT_TRUE(absl_log_internal_qcheck_ok_goo.first->ok()) \ + ? nullptr \ + : ::absl::status_internal::MakeCheckFailString( \ + absl_log_internal_qcheck_ok_goo.first, \ + ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(val_text \ + " is OK")), \ + !ABSL_PREDICT_TRUE(absl_log_internal_qcheck_ok_goo.first->ok());) \ + ABSL_LOG_INTERNAL_CONDITION_QFATAL(STATELESS, true) \ + ABSL_LOG_INTERNAL_QCHECK(*absl_log_internal_qcheck_ok_goo.second) \ .InternalStream() namespace absl { @@ -152,8 +162,8 @@ class StatusOr; namespace status_internal { -std::string* MakeCheckFailString(const absl::Status* status, - const char* prefix); +ABSL_ATTRIBUTE_PURE_FUNCTION std::string* MakeCheckFailString( + const absl::Status* status, const char* prefix); } // namespace status_internal namespace log_internal { @@ -314,6 +324,20 @@ ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(const void*); #undef ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN +// `ABSL_LOG_INTERNAL_CHECK_OP_IMPL_RESULT` skips formatting the Check_OP result +// string iff `ABSL_MIN_LOG_LEVEL` exceeds `kFatal`, instead returning an empty +// string. +#ifdef ABSL_MIN_LOG_LEVEL +#define ABSL_LOG_INTERNAL_CHECK_OP_IMPL_RESULT(U1, U2, v1, v2, exprtext) \ + ((::absl::LogSeverity::kFatal >= \ + static_cast<::absl::LogSeverity>(ABSL_MIN_LOG_LEVEL)) \ + ? MakeCheckOpString<U1, U2>(v1, v2, exprtext) \ + : new std::string()) +#else +#define ABSL_LOG_INTERNAL_CHECK_OP_IMPL_RESULT(U1, U2, v1, v2, exprtext) \ + MakeCheckOpString<U1, U2>(v1, v2, exprtext) +#endif + // Helper functions for `ABSL_LOG_INTERNAL_CHECK_OP` macro family. The // `(int, int)` override works around the issue that the compiler will not // instantiate the template version of the function on values of unnamed enum @@ -326,7 +350,8 @@ using U2 = CheckOpStreamType<T2>; \ return ABSL_PREDICT_TRUE(v1 op v2) \ ? nullptr \ - : MakeCheckOpString<U1, U2>(v1, v2, exprtext); \ + : ABSL_LOG_INTERNAL_CHECK_OP_IMPL_RESULT(U1, U2, v1, v2, \ + exprtext); \ } \ inline constexpr ::std::string* name##Impl(int v1, int v2, \ const char* exprtext) { \ @@ -339,6 +364,7 @@ ABSL_LOG_INTERNAL_CHECK_OP_IMPL(Check_LT, <) ABSL_LOG_INTERNAL_CHECK_OP_IMPL(Check_GE, >=) ABSL_LOG_INTERNAL_CHECK_OP_IMPL(Check_GT, >) +#undef ABSL_LOG_INTERNAL_CHECK_OP_IMPL_RESULT #undef ABSL_LOG_INTERNAL_CHECK_OP_IMPL std::string* CheckstrcmptrueImpl(const char* s1, const char* s2,
diff --git a/absl/log/internal/conditions.h b/absl/log/internal/conditions.h index 41f6721..45a17b0 100644 --- a/absl/log/internal/conditions.h +++ b/absl/log/internal/conditions.h
@@ -68,7 +68,7 @@ !(condition) ? (void)0 : ::absl::log_internal::Voidify()&& // `ABSL_LOG_INTERNAL_STATEFUL_CONDITION` applies a condition like -// `ABSL_LOG_INTERNAL_CONDITION` but adds to that a series of variable +// `ABSL_LOG_INTERNAL_STATELESS_CONDITION` but adds to that a series of variable // declarations, including a local static object which stores the state needed // to implement the stateful macros like `LOG_EVERY_N`. // @@ -147,20 +147,20 @@ (::absl::kLogDebugFatal == ::absl::LogSeverity::kFatal && \ (::absl::log_internal::AbortQuietly(), false))))) -#define ABSL_LOG_INTERNAL_CONDITION_LEVEL(severity) \ - for (int log_internal_severity_loop = 1; log_internal_severity_loop; \ - log_internal_severity_loop = 0) \ - for (const absl::LogSeverity log_internal_severity = \ - ::absl::NormalizeLogSeverity(severity); \ - log_internal_severity_loop; log_internal_severity_loop = 0) \ +#define ABSL_LOG_INTERNAL_CONDITION_LEVEL(severity) \ + for (int absl_log_internal_severity_loop = 1; \ + absl_log_internal_severity_loop; absl_log_internal_severity_loop = 0) \ + for (const absl::LogSeverity absl_log_internal_severity = \ + ::absl::NormalizeLogSeverity(severity); \ + absl_log_internal_severity_loop; absl_log_internal_severity_loop = 0) \ ABSL_LOG_INTERNAL_CONDITION_LEVEL_IMPL -#define ABSL_LOG_INTERNAL_CONDITION_LEVEL_IMPL(type, condition) \ - ABSL_LOG_INTERNAL_##type##_CONDITION( \ - (condition) && \ - (log_internal_severity >= \ - static_cast<::absl::LogSeverity>(ABSL_MIN_LOG_LEVEL) || \ - (log_internal_severity == ::absl::LogSeverity::kFatal && \ - (::absl::log_internal::AbortQuietly(), false)))) +#define ABSL_LOG_INTERNAL_CONDITION_LEVEL_IMPL(type, condition) \ + ABSL_LOG_INTERNAL_##type##_CONDITION(( \ + (condition) && \ + (absl_log_internal_severity >= \ + static_cast<::absl::LogSeverity>(ABSL_MIN_LOG_LEVEL) || \ + (absl_log_internal_severity == ::absl::LogSeverity::kFatal && \ + (::absl::log_internal::AbortQuietly(), false))))) #else // ndef ABSL_MIN_LOG_LEVEL #define ABSL_LOG_INTERNAL_CONDITION_INFO(type, condition) \ ABSL_LOG_INTERNAL_##type##_CONDITION(condition) @@ -174,12 +174,12 @@ ABSL_LOG_INTERNAL_##type##_CONDITION(condition) #define ABSL_LOG_INTERNAL_CONDITION_DFATAL(type, condition) \ ABSL_LOG_INTERNAL_##type##_CONDITION(condition) -#define ABSL_LOG_INTERNAL_CONDITION_LEVEL(severity) \ - for (int log_internal_severity_loop = 1; log_internal_severity_loop; \ - log_internal_severity_loop = 0) \ - for (const absl::LogSeverity log_internal_severity = \ - ::absl::NormalizeLogSeverity(severity); \ - log_internal_severity_loop; log_internal_severity_loop = 0) \ +#define ABSL_LOG_INTERNAL_CONDITION_LEVEL(severity) \ + for (int absl_log_internal_severity_loop = 1; \ + absl_log_internal_severity_loop; absl_log_internal_severity_loop = 0) \ + for (const absl::LogSeverity absl_log_internal_severity = \ + ::absl::NormalizeLogSeverity(severity); \ + absl_log_internal_severity_loop; absl_log_internal_severity_loop = 0) \ ABSL_LOG_INTERNAL_CONDITION_LEVEL_IMPL #define ABSL_LOG_INTERNAL_CONDITION_LEVEL_IMPL(type, condition) \ ABSL_LOG_INTERNAL_##type##_CONDITION(condition)
diff --git a/absl/log/internal/flags.h b/absl/log/internal/flags.h index b91b864..c453978 100644 --- a/absl/log/internal/flags.h +++ b/absl/log/internal/flags.h
@@ -50,4 +50,10 @@ // each message logged. Defaults to true. ABSL_DECLARE_FLAG(bool, log_prefix); +// Global log verbosity level. Default is 0. +ABSL_DECLARE_FLAG(int, v); + +// Per-module log verbosity level. By default is empty and is unused. +ABSL_DECLARE_FLAG(std::string, vmodule); + #endif // ABSL_LOG_INTERNAL_FLAGS_H_
diff --git a/absl/log/internal/log_impl.h b/absl/log/internal/log_impl.h index 9326780..99de6db 100644 --- a/absl/log/internal/log_impl.h +++ b/absl/log/internal/log_impl.h
@@ -15,6 +15,7 @@ #ifndef ABSL_LOG_INTERNAL_LOG_IMPL_H_ #define ABSL_LOG_INTERNAL_LOG_IMPL_H_ +#include "absl/log/absl_vlog_is_on.h" #include "absl/log/internal/conditions.h" #include "absl/log/internal/log_message.h" #include "absl/log/internal/strip.h" @@ -41,6 +42,35 @@ ABSL_LOGGING_INTERNAL_LOG##severity.InternalStream() #endif +// The `switch` ensures that this expansion is the begnning of a statement (as +// opposed to an expression). The use of both `case 0` and `default` is to +// suppress a compiler warning. +#define ABSL_LOG_INTERNAL_VLOG_IMPL(verbose_level) \ + switch (const int absl_logging_internal_verbose_level = (verbose_level)) \ + case 0: \ + default: \ + ABSL_LOG_INTERNAL_LOG_IF_IMPL( \ + _INFO, ABSL_VLOG_IS_ON(absl_logging_internal_verbose_level)) \ + .WithVerbosity(absl_logging_internal_verbose_level) + +#ifndef NDEBUG +#define ABSL_LOG_INTERNAL_DVLOG_IMPL(verbose_level) \ + switch (const int absl_logging_internal_verbose_level = (verbose_level)) \ + case 0: \ + default: \ + ABSL_LOG_INTERNAL_LOG_IF_IMPL( \ + _INFO, ABSL_VLOG_IS_ON(absl_logging_internal_verbose_level)) \ + .WithVerbosity(absl_logging_internal_verbose_level) +#else +#define ABSL_LOG_INTERNAL_DVLOG_IMPL(verbose_level) \ + switch (const int absl_logging_internal_verbose_level = (verbose_level)) \ + case 0: \ + default: \ + ABSL_LOG_INTERNAL_LOG_IF_IMPL( \ + _INFO, false && ABSL_VLOG_IS_ON(absl_logging_internal_verbose_level)) \ + .WithVerbosity(absl_logging_internal_verbose_level) +#endif + #define ABSL_LOG_INTERNAL_LOG_IF_IMPL(severity, condition) \ ABSL_LOG_INTERNAL_CONDITION##severity(STATELESS, condition) \ ABSL_LOGGING_INTERNAL_LOG##severity.InternalStream() @@ -134,6 +164,42 @@ (EveryNSec, n_seconds) ABSL_LOGGING_INTERNAL_LOG##severity.InternalStream() #endif // def NDEBUG +#define ABSL_LOG_INTERNAL_VLOG_EVERY_N_IMPL(verbose_level, n) \ + switch (const int absl_logging_internal_verbose_level = (verbose_level)) \ + case 0: \ + default: \ + ABSL_LOG_INTERNAL_CONDITION_INFO( \ + STATEFUL, ABSL_VLOG_IS_ON(absl_logging_internal_verbose_level)) \ + (EveryN, n) ABSL_LOGGING_INTERNAL_LOG_INFO.InternalStream().WithVerbosity( \ + absl_logging_internal_verbose_level) + +#define ABSL_LOG_INTERNAL_VLOG_FIRST_N_IMPL(verbose_level, n) \ + switch (const int absl_logging_internal_verbose_level = (verbose_level)) \ + case 0: \ + default: \ + ABSL_LOG_INTERNAL_CONDITION_INFO( \ + STATEFUL, ABSL_VLOG_IS_ON(absl_logging_internal_verbose_level)) \ + (FirstN, n) ABSL_LOGGING_INTERNAL_LOG_INFO.InternalStream().WithVerbosity( \ + absl_logging_internal_verbose_level) + +#define ABSL_LOG_INTERNAL_VLOG_EVERY_POW_2_IMPL(verbose_level) \ + switch (const int absl_logging_internal_verbose_level = (verbose_level)) \ + case 0: \ + default: \ + ABSL_LOG_INTERNAL_CONDITION_INFO( \ + STATEFUL, ABSL_VLOG_IS_ON(absl_logging_internal_verbose_level)) \ + (EveryPow2) ABSL_LOGGING_INTERNAL_LOG_INFO.InternalStream().WithVerbosity( \ + absl_logging_internal_verbose_level) + +#define ABSL_LOG_INTERNAL_VLOG_EVERY_N_SEC_IMPL(verbose_level, n_seconds) \ + switch (const int absl_logging_internal_verbose_level = (verbose_level)) \ + case 0: \ + default: \ + ABSL_LOG_INTERNAL_CONDITION_INFO( \ + STATEFUL, ABSL_VLOG_IS_ON(absl_logging_internal_verbose_level)) \ + (EveryNSec, n_seconds) ABSL_LOGGING_INTERNAL_LOG_INFO.InternalStream() \ + .WithVerbosity(absl_logging_internal_verbose_level) + #define ABSL_LOG_INTERNAL_LOG_IF_EVERY_N_IMPL(severity, condition, n) \ ABSL_LOG_INTERNAL_CONDITION##severity(STATEFUL, condition)(EveryN, n) \ ABSL_LOGGING_INTERNAL_LOG##severity.InternalStream()
diff --git a/absl/log/internal/log_message.h b/absl/log/internal/log_message.h index c94d053..4ecb8a1 100644 --- a/absl/log/internal/log_message.h +++ b/absl/log/internal/log_message.h
@@ -170,13 +170,15 @@ // Types that support `AbslStringify()` are serialized that way. template <typename T, - typename std::enable_if<HasAbslStringify<T>::value, int>::type = 0> + typename std::enable_if<absl::HasAbslStringify<T>::value, + int>::type = 0> LogMessage& operator<<(const T& v) ABSL_ATTRIBUTE_NOINLINE; // Types that don't support `AbslStringify()` but do support streaming into a // `std::ostream&` are serialized that way. template <typename T, - typename std::enable_if<!HasAbslStringify<T>::value, int>::type = 0> + typename std::enable_if<!absl::HasAbslStringify<T>::value, + int>::type = 0> LogMessage& operator<<(const T& v) ABSL_ATTRIBUTE_NOINLINE; // Note: We explicitly do not support `operator<<` for non-const references @@ -281,7 +283,7 @@ // Note: the following is declared `ABSL_ATTRIBUTE_NOINLINE` template <typename T, - typename std::enable_if<HasAbslStringify<T>::value, int>::type> + typename std::enable_if<absl::HasAbslStringify<T>::value, int>::type> LogMessage& LogMessage::operator<<(const T& v) { StringifySink sink(*this); // Replace with public API. @@ -291,7 +293,7 @@ // Note: the following is declared `ABSL_ATTRIBUTE_NOINLINE` template <typename T, - typename std::enable_if<!HasAbslStringify<T>::value, int>::type> + typename std::enable_if<!absl::HasAbslStringify<T>::value, int>::type> LogMessage& LogMessage::operator<<(const T& v) { OstreamView view(*data_); view.stream() << log_internal::NullGuard<T>().Guard(v);
diff --git a/absl/log/internal/strip.h b/absl/log/internal/strip.h index adc86ff..f8d2786 100644 --- a/absl/log/internal/strip.h +++ b/absl/log/internal/strip.h
@@ -37,7 +37,7 @@ #define ABSL_LOGGING_INTERNAL_LOG_DFATAL \ ::absl::log_internal::NullStreamMaybeFatal(::absl::kLogDebugFatal) #define ABSL_LOGGING_INTERNAL_LOG_LEVEL(severity) \ - ::absl::log_internal::NullStreamMaybeFatal(log_internal_severity) + ::absl::log_internal::NullStreamMaybeFatal(absl_log_internal_severity) #define ABSL_LOG_INTERNAL_CHECK(failure_message) ABSL_LOGGING_INTERNAL_LOG_FATAL #define ABSL_LOG_INTERNAL_QCHECK(failure_message) \ ABSL_LOGGING_INTERNAL_LOG_QFATAL @@ -57,8 +57,9 @@ ::absl::log_internal::LogMessageQuietlyFatal(__FILE__, __LINE__) #define ABSL_LOGGING_INTERNAL_LOG_DFATAL \ ::absl::log_internal::LogMessage(__FILE__, __LINE__, ::absl::kLogDebugFatal) -#define ABSL_LOGGING_INTERNAL_LOG_LEVEL(severity) \ - ::absl::log_internal::LogMessage(__FILE__, __LINE__, log_internal_severity) +#define ABSL_LOGGING_INTERNAL_LOG_LEVEL(severity) \ + ::absl::log_internal::LogMessage(__FILE__, __LINE__, \ + absl_log_internal_severity) // These special cases dispatch to special-case constructors that allow us to // avoid an extra function call and shrink non-LTO binaries by a percent or so. #define ABSL_LOG_INTERNAL_CHECK(failure_message) \
diff --git a/absl/log/internal/vlog_config.cc b/absl/log/internal/vlog_config.cc new file mode 100644 index 0000000..b578850 --- /dev/null +++ b/absl/log/internal/vlog_config.cc
@@ -0,0 +1,340 @@ +// Copyright 2022 The Abseil Authors +// +// Licensed under the Apache License, Version 2.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/log/internal/vlog_config.h" + +#include <stddef.h> + +#include <algorithm> +#include <atomic> +#include <functional> +#include <memory> +#include <string> +#include <utility> +#include <vector> + +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "absl/base/const_init.h" +#include "absl/base/internal/spinlock.h" +#include "absl/base/no_destructor.h" +#include "absl/base/optimization.h" +#include "absl/base/thread_annotations.h" +#include "absl/log/internal/fnmatch.h" +#include "absl/memory/memory.h" +#include "absl/strings/numbers.h" +#include "absl/strings/str_split.h" +#include "absl/strings/string_view.h" +#include "absl/strings/strip.h" +#include "absl/synchronization/mutex.h" +#include "absl/types/optional.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace log_internal { + +namespace { +bool ModuleIsPath(absl::string_view module_pattern) { +#ifdef _WIN32 + return module_pattern.find_first_of("/\\") != module_pattern.npos; +#else + return module_pattern.find('/') != module_pattern.npos; +#endif +} +} // namespace + +bool VLogSite::SlowIsEnabled(int stale_v, int level) { + if (ABSL_PREDICT_TRUE(stale_v != kUninitialized)) { + // Because of the prerequisites to this function, we know that stale_v is + // either uninitialized or >= level. If it's not uninitialized, that means + // it must be >= level, thus we should log. + return true; + } + stale_v = log_internal::RegisterAndInitialize(this); + return ABSL_PREDICT_FALSE(stale_v >= level); +} + +bool VLogSite::SlowIsEnabled0(int stale_v) { return SlowIsEnabled(stale_v, 0); } +bool VLogSite::SlowIsEnabled1(int stale_v) { return SlowIsEnabled(stale_v, 1); } +bool VLogSite::SlowIsEnabled2(int stale_v) { return SlowIsEnabled(stale_v, 2); } +bool VLogSite::SlowIsEnabled3(int stale_v) { return SlowIsEnabled(stale_v, 3); } +bool VLogSite::SlowIsEnabled4(int stale_v) { return SlowIsEnabled(stale_v, 4); } +bool VLogSite::SlowIsEnabled5(int stale_v) { return SlowIsEnabled(stale_v, 5); } + +namespace { +struct VModuleInfo final { + std::string module_pattern; + bool module_is_path; // i.e. it contains a path separator. + int vlog_level; + + // Allocates memory. + VModuleInfo(absl::string_view module_pattern_arg, bool module_is_path_arg, + int vlog_level_arg) + : module_pattern(std::string(module_pattern_arg)), + module_is_path(module_is_path_arg), + vlog_level(vlog_level_arg) {} +}; + +// `mutex` guards all of the data structures that aren't lock-free. +// To avoid problems with the heap checker which calls into `VLOG`, `mutex` must +// be a `SpinLock` that prevents fiber scheduling instead of a `Mutex`. +ABSL_CONST_INIT absl::base_internal::SpinLock mutex( + absl::kConstInit, absl::base_internal::SCHEDULE_KERNEL_ONLY); + +// `GetUpdateSitesMutex()` serializes updates to all of the sites (i.e. those in +// `site_list_head`) themselves. +absl::Mutex* GetUpdateSitesMutex() { + // Chromium requires no global destructors, so we can't use the + // absl::kConstInit idiom since absl::Mutex as a non-trivial destructor. + static absl::NoDestructor<absl::Mutex> update_sites_mutex ABSL_ACQUIRED_AFTER( + mutex); + return update_sites_mutex.get(); +} + +ABSL_CONST_INIT int global_v ABSL_GUARDED_BY(mutex) = 0; +// `site_list_head` is the head of a singly-linked list. Traversal, insertion, +// and reads are atomic, so no locks are required, but updates to existing +// elements are guarded by `GetUpdateSitesMutex()`. +ABSL_CONST_INIT std::atomic<VLogSite*> site_list_head{nullptr}; +ABSL_CONST_INIT std::vector<VModuleInfo>* vmodule_info ABSL_GUARDED_BY(mutex) + ABSL_PT_GUARDED_BY(mutex){nullptr}; + +// Only used for lisp. +ABSL_CONST_INIT std::vector<std::function<void()>>* update_callbacks + ABSL_GUARDED_BY(GetUpdateSitesMutex()) + ABSL_PT_GUARDED_BY(GetUpdateSitesMutex()){nullptr}; + +// Allocates memory. +std::vector<VModuleInfo>& get_vmodule_info() + ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex) { + if (!vmodule_info) vmodule_info = new std::vector<VModuleInfo>; + return *vmodule_info; +} + +// Does not allocate or take locks. +int VLogLevel(absl::string_view file, const std::vector<VModuleInfo>* infos, + int current_global_v) { + // `infos` is null during a call to `VLOG` prior to setting `vmodule` (e.g. by + // parsing flags). We can't allocate in `VLOG`, so we treat null as empty + // here and press on. + if (!infos || infos->empty()) return current_global_v; + // Get basename for file + absl::string_view basename = file; + { + const size_t sep = basename.rfind('/'); + if (sep != basename.npos) { + basename.remove_prefix(sep + 1); +#ifdef _WIN32 + } else { + const size_t sep = basename.rfind('\\'); + if (sep != basename.npos) basename.remove_prefix(sep + 1); +#endif + } + } + + absl::string_view stem = file, stem_basename = basename; + { + const size_t sep = stem_basename.find('.'); + if (sep != stem_basename.npos) { + stem.remove_suffix(stem_basename.size() - sep); + stem_basename.remove_suffix(stem_basename.size() - sep); + } + if (absl::ConsumeSuffix(&stem_basename, "-inl")) { + stem.remove_suffix(absl::string_view("-inl").size()); + } + } + for (const auto& info : *infos) { + if (info.module_is_path) { + // If there are any slashes in the pattern, try to match the full + // name. + if (FNMatch(info.module_pattern, stem)) { + return info.vlog_level == kUseFlag ? current_global_v : info.vlog_level; + } + } else if (FNMatch(info.module_pattern, stem_basename)) { + return info.vlog_level == kUseFlag ? current_global_v : info.vlog_level; + } + } + + return current_global_v; +} + +// Allocates memory. +int AppendVModuleLocked(absl::string_view module_pattern, int log_level) + ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex) { + for (const auto& info : get_vmodule_info()) { + if (FNMatch(info.module_pattern, module_pattern)) { + // This is a memory optimization to avoid storing patterns that will never + // match due to exit early semantics. Primarily optimized for our own unit + // tests. + return info.vlog_level; + } + } + bool module_is_path = ModuleIsPath(module_pattern); + get_vmodule_info().emplace_back(std::string(module_pattern), module_is_path, + log_level); + return global_v; +} + +// Allocates memory. +int PrependVModuleLocked(absl::string_view module_pattern, int log_level) + ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex) { + absl::optional<int> old_log_level; + for (const auto& info : get_vmodule_info()) { + if (FNMatch(info.module_pattern, module_pattern)) { + old_log_level = info.vlog_level; + break; + } + } + bool module_is_path = ModuleIsPath(module_pattern); + auto iter = get_vmodule_info().emplace(get_vmodule_info().cbegin(), + std::string(module_pattern), + module_is_path, log_level); + + // This is a memory optimization to avoid storing patterns that will never + // match due to exit early semantics. Primarily optimized for our own unit + // tests. + get_vmodule_info().erase( + std::remove_if(++iter, get_vmodule_info().end(), + [module_pattern](const VModuleInfo& info) { + return FNMatch(info.module_pattern, module_pattern); + }), + get_vmodule_info().cend()); + return old_log_level.value_or(global_v); +} +} // namespace + +int VLogLevel(absl::string_view file) ABSL_LOCKS_EXCLUDED(mutex) { + absl::base_internal::SpinLockHolder l(&mutex); + return VLogLevel(file, vmodule_info, global_v); +} + +int RegisterAndInitialize(VLogSite* v) ABSL_LOCKS_EXCLUDED(mutex) { + // std::memory_order_seq_cst is overkill in this function, but given that this + // path is intended to be slow, it's not worth the brain power to relax that. + VLogSite* h = site_list_head.load(std::memory_order_seq_cst); + + VLogSite* old = nullptr; + if (v->next_.compare_exchange_strong(old, h, std::memory_order_seq_cst, + std::memory_order_seq_cst)) { + // Multiple threads may attempt to register this site concurrently. + // By successfully setting `v->next` this thread commits to being *the* + // thread that installs `v` in the list. + while (!site_list_head.compare_exchange_weak( + h, v, std::memory_order_seq_cst, std::memory_order_seq_cst)) { + v->next_.store(h, std::memory_order_seq_cst); + } + } + + int old_v = VLogSite::kUninitialized; + int new_v = VLogLevel(v->file_); + // No loop, if someone else set this, we should respect their evaluation of + // `VLogLevel`. This may mean we return a stale `v`, but `v` itself will + // always arrive at the freshest value. Otherwise, we could be writing a + // stale value and clobbering the fresher one. + if (v->v_.compare_exchange_strong(old_v, new_v, std::memory_order_seq_cst, + std::memory_order_seq_cst)) { + return new_v; + } + return old_v; +} + +void UpdateVLogSites() ABSL_UNLOCK_FUNCTION(mutex) + ABSL_LOCKS_EXCLUDED(GetUpdateSitesMutex()) { + std::vector<VModuleInfo> infos = get_vmodule_info(); + int current_global_v = global_v; + // We need to grab `GetUpdateSitesMutex()` before we release `mutex` to ensure + // that updates are not interleaved (resulting in an inconsistent final state) + // and to ensure that the final state in the sites matches the final state of + // `vmodule_info`. We unlock `mutex` to ensure that uninitialized sites don't + // have to wait on all updates in order to acquire `mutex` and initialize + // themselves. + absl::MutexLock ul(GetUpdateSitesMutex()); + mutex.Unlock(); + VLogSite* n = site_list_head.load(std::memory_order_seq_cst); + // Because sites are added to the list in the order they are executed, there + // tend to be clusters of entries with the same file. + const char* last_file = nullptr; + int last_file_level = 0; + while (n != nullptr) { + if (n->file_ != last_file) { + last_file = n->file_; + last_file_level = VLogLevel(n->file_, &infos, current_global_v); + } + n->v_.store(last_file_level, std::memory_order_seq_cst); + n = n->next_.load(std::memory_order_seq_cst); + } + if (update_callbacks) { + for (auto& cb : *update_callbacks) { + cb(); + } + } +} + +void UpdateVModule(absl::string_view vmodule) + ABSL_LOCKS_EXCLUDED(mutex, GetUpdateSitesMutex()) { + std::vector<std::pair<absl::string_view, int>> glob_levels; + for (absl::string_view glob_level : absl::StrSplit(vmodule, ',')) { + const size_t eq = glob_level.rfind('='); + if (eq == glob_level.npos) continue; + const absl::string_view glob = glob_level.substr(0, eq); + int level; + if (!absl::SimpleAtoi(glob_level.substr(eq + 1), &level)) continue; + glob_levels.emplace_back(glob, level); + } + mutex.Lock(); // Unlocked by UpdateVLogSites(). + get_vmodule_info().clear(); + for (const auto& it : glob_levels) { + const absl::string_view glob = it.first; + const int level = it.second; + AppendVModuleLocked(glob, level); + } + UpdateVLogSites(); +} + +int UpdateGlobalVLogLevel(int v) + ABSL_LOCKS_EXCLUDED(mutex, GetUpdateSitesMutex()) { + mutex.Lock(); // Unlocked by UpdateVLogSites(). + const int old_global_v = global_v; + if (v == global_v) { + mutex.Unlock(); + return old_global_v; + } + global_v = v; + UpdateVLogSites(); + return old_global_v; +} + +int PrependVModule(absl::string_view module_pattern, int log_level) + ABSL_LOCKS_EXCLUDED(mutex, GetUpdateSitesMutex()) { + mutex.Lock(); // Unlocked by UpdateVLogSites(). + int old_v = PrependVModuleLocked(module_pattern, log_level); + UpdateVLogSites(); + return old_v; +} + +void OnVLogVerbosityUpdate(std::function<void()> cb) + ABSL_LOCKS_EXCLUDED(GetUpdateSitesMutex()) { + absl::MutexLock ul(GetUpdateSitesMutex()); + if (!update_callbacks) + update_callbacks = new std::vector<std::function<void()>>; + update_callbacks->push_back(std::move(cb)); +} + +VLogSite* SetVModuleListHeadForTestOnly(VLogSite* v) { + return site_list_head.exchange(v, std::memory_order_seq_cst); +} + +} // namespace log_internal +ABSL_NAMESPACE_END +} // namespace absl
diff --git a/absl/log/internal/vlog_config.h b/absl/log/internal/vlog_config.h new file mode 100644 index 0000000..b6e322c --- /dev/null +++ b/absl/log/internal/vlog_config.h
@@ -0,0 +1,163 @@ +// Copyright 2022 The Abseil Authors +// +// Licensed under the Apache License, Version 2.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. +// +// ----------------------------------------------------------------------------- +// vlog_config.h +// ----------------------------------------------------------------------------- +// +// This header file defines `VLogSite`, a public primitive that represents +// a callsite for the `VLOG` family of macros and related libraries. +// It also declares and defines multiple internal utilities used to implement +// `VLOG`, such as `VLogSiteManager`. + +#ifndef ABSL_LOG_INTERNAL_VLOG_CONFIG_H_ +#define ABSL_LOG_INTERNAL_VLOG_CONFIG_H_ + +// IWYU pragma: private, include "absl/log/log.h" + +#include <atomic> +#include <cstdint> +#include <functional> +#include <limits> +#include <type_traits> + +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "absl/base/optimization.h" +#include "absl/base/thread_annotations.h" +#include "absl/strings/string_view.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace log_internal { + +class SyntheticBinary; +class VLogSite; + +int RegisterAndInitialize(VLogSite* v); +void UpdateVLogSites(); +constexpr int kUseFlag = (std::numeric_limits<int16_t>::min)(); + +// Represents a unique callsite for a `VLOG()` or `VLOG_IS_ON()` call. +// +// Libraries that provide `VLOG`-like functionality should use this to +// efficiently handle --vmodule. +// +// VLogSite objects must not be destroyed until the program exits. Doing so will +// probably yield nasty segfaults in VLogSiteManager::UpdateLogSites(). The +// recommendation is to make all such objects function-local statics. +class VLogSite final { + public: + // `f` must not be destroyed until the program exits. + explicit constexpr VLogSite(const char* f) + : file_(f), v_(kUninitialized), next_(nullptr) {} + VLogSite(const VLogSite&) = delete; + VLogSite& operator=(const VLogSite&) = delete; + + // Inlining the function yields a ~3x performance improvement at the cost of a + // 1.5x code size increase at the call site. + // Takes locks but does not allocate memory. + ABSL_ATTRIBUTE_ALWAYS_INLINE + bool IsEnabled(int level) { + int stale_v = v_.load(std::memory_order_relaxed); + if (ABSL_PREDICT_TRUE(level > stale_v)) { + return false; + } + + // We put everything other than the fast path, i.e. vlogging is initialized + // but not on, behind an out-of-line function to reduce code size. + // "level" is almost always a call-site constant, so we can save a bit + // of code space by special-casing for a few common levels. +#if ABSL_HAVE_BUILTIN(__builtin_constant_p) || defined(__GNUC__) + if (__builtin_constant_p(level)) { + if (level == 0) return SlowIsEnabled0(stale_v); + if (level == 1) return SlowIsEnabled1(stale_v); + if (level == 2) return SlowIsEnabled2(stale_v); + if (level == 3) return SlowIsEnabled3(stale_v); + if (level == 4) return SlowIsEnabled4(stale_v); + if (level == 5) return SlowIsEnabled5(stale_v); + } +#endif + return SlowIsEnabled(stale_v, level); + } + + private: + friend int log_internal::RegisterAndInitialize(VLogSite* v); + friend void log_internal::UpdateVLogSites(); + friend class log_internal::SyntheticBinary; + static constexpr int kUninitialized = (std::numeric_limits<int>::max)(); + + // SlowIsEnabled performs slower checks to determine whether a log site is + // enabled. Because it is expected to be called somewhat rarely + // (comparatively), it is not inlined to save on code size. + // + // Prerequisites to calling SlowIsEnabled: + // 1) stale_v is uninitialized OR + // 2) stale_v is initialized and >= level (meaning we must log). + // Takes locks but does not allocate memory. + ABSL_ATTRIBUTE_NOINLINE + bool SlowIsEnabled(int stale_v, int level); + ABSL_ATTRIBUTE_NOINLINE bool SlowIsEnabled0(int stale_v); + ABSL_ATTRIBUTE_NOINLINE bool SlowIsEnabled1(int stale_v); + ABSL_ATTRIBUTE_NOINLINE bool SlowIsEnabled2(int stale_v); + ABSL_ATTRIBUTE_NOINLINE bool SlowIsEnabled3(int stale_v); + ABSL_ATTRIBUTE_NOINLINE bool SlowIsEnabled4(int stale_v); + ABSL_ATTRIBUTE_NOINLINE bool SlowIsEnabled5(int stale_v); + + // This object is too size-sensitive to use absl::string_view. + const char* const file_; + std::atomic<int> v_; + std::atomic<VLogSite*> next_; +}; +static_assert(std::is_trivially_destructible<VLogSite>::value, + "VLogSite must be trivially destructible"); + +// Returns the current verbose log level of `file`. +// Does not allocate memory. +int VLogLevel(absl::string_view file); + +// Registers a site `v` to get updated as `vmodule` and `v` change. Also +// initializes the site based on their current values, and returns that result. +// Does not allocate memory. +int RegisterAndInitialize(VLogSite* v); + +// Allocates memory. +void UpdateVLogSites(); + +// Completely overwrites the saved value of `vmodule`. +// Allocates memory. +void UpdateVModule(absl::string_view vmodule); + +// Updates the global verbosity level to `v` and returns the prior value. +// Allocates memory. +int UpdateGlobalVLogLevel(int v); + +// Atomically prepends `module_pattern=log_level` to the start of vmodule. +// Returns the prior value for `module_pattern` if there was an exact match and +// `global_v` otherwise. +// Allocates memory. +int PrependVModule(absl::string_view module_pattern, int log_level); + +// Registers `on_update` to be called whenever `v` or `vmodule` change. +// Allocates memory. +void OnVLogVerbosityUpdate(std::function<void()> cb); + +// Does not allocate memory. +VLogSite* SetVModuleListHeadForTestOnly(VLogSite* v); + +} // namespace log_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_LOG_INTERNAL_VLOG_CONFIG_H_
diff --git a/absl/log/internal/vlog_config_benchmark.cc b/absl/log/internal/vlog_config_benchmark.cc new file mode 100644 index 0000000..9004e2e --- /dev/null +++ b/absl/log/internal/vlog_config_benchmark.cc
@@ -0,0 +1,187 @@ +// Copyright 2022 The Abseil Authors +// +// Licensed under the Apache License, Version 2.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 <algorithm> +#include <atomic> +#include <cstddef> +#include <cstring> +#include <memory> +#include <new> +#include <random> +#include <string> +#include <type_traits> +#include <utility> +#include <vector> + +#include "absl/base/config.h" +#include "absl/container/internal/layout.h" +#include "absl/log/internal/vlog_config.h" +#include "absl/memory/memory.h" +#include "absl/random/distributions.h" +#include "absl/strings/str_cat.h" +#include "benchmark/benchmark.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace log_internal { +// Performance of `UpdateLogSites` depends upon the number and organization of +// `VLogSite`s in the program. We can synthesize some on the heap to mimic +// their layout and linkage in static data. +class SyntheticBinary { + public: + explicit SyntheticBinary(const size_t num_tus, + const size_t max_extra_static_data_bytes_per_tu, + const size_t max_sites_per_tu, + const int num_shuffles) { + per_tu_data_.reserve(num_tus); + auto sites = absl::make_unique<VLogSite *[]>(num_tus * max_sites_per_tu); + for (size_t i = 0; i < num_tus; i++) { + const std::string filename = + absl::StrCat("directory-", i / 100, "/subdirectory-", i % 100 / 10, + "/file-", i % 10, ".cc"); + container_internal::Layout<char, VLogSite, char> layout( + filename.size() + 1, + absl::LogUniform<size_t>(bitgen_, 1, max_sites_per_tu), + absl::LogUniform<size_t>(bitgen_, 0, + max_extra_static_data_bytes_per_tu)); + auto buf = absl::make_unique<char[]>(layout.AllocSize()); + layout.PoisonPadding(buf.get()); + memcpy(layout.Pointer<0>(buf.get()), filename.c_str(), + filename.size() + 1); + for (VLogSite &site : layout.Slice<1>(buf.get())) { + sites[num_sites_++] = + new (&site) VLogSite(layout.Pointer<0>(buf.get())); + // The last one is a dangling pointer but will be fixed below. + site.next_.store(&site + 1, std::memory_order_seq_cst); + } + num_extra_static_data_bytes_ += layout.Size<2>(); + per_tu_data_.push_back(PerTU{layout, std::move(buf)}); + } + // Now link the files together back-to-front into a circular list. + for (size_t i = 0; i < num_tus; i++) { + auto &tu = per_tu_data_[i]; + auto &next_tu = per_tu_data_[(i + 1) % num_tus]; + tu.layout.Slice<1>(tu.buf.get()) + .back() + .next_.store(next_tu.layout.Pointer<1>(next_tu.buf.get()), + std::memory_order_seq_cst); + } + // Now do some shufflin'. + auto new_sites = absl::make_unique<VLogSite *[]>(num_sites_); + for (int shuffle_num = 0; shuffle_num < num_shuffles; shuffle_num++) { + // Each shuffle cuts the ring into three pieces and rearranges them. + const size_t cut_a = absl::Uniform(bitgen_, size_t{0}, num_sites_); + const size_t cut_b = absl::Uniform(bitgen_, size_t{0}, num_sites_); + const size_t cut_c = absl::Uniform(bitgen_, size_t{0}, num_sites_); + if (cut_a == cut_b || cut_b == cut_c || cut_a == cut_c) continue; + // The same cuts, sorted: + const size_t cut_1 = std::min({cut_a, cut_b, cut_c}); + const size_t cut_3 = std::max({cut_a, cut_b, cut_c}); + const size_t cut_2 = cut_a ^ cut_b ^ cut_c ^ cut_1 ^ cut_3; + VLogSite *const tmp = sites[cut_1]->next_.load(std::memory_order_seq_cst); + sites[cut_1]->next_.store( + sites[cut_2]->next_.load(std::memory_order_seq_cst), + std::memory_order_seq_cst); + sites[cut_2]->next_.store( + sites[cut_3]->next_.load(std::memory_order_seq_cst), + std::memory_order_seq_cst); + sites[cut_3]->next_.store(tmp, std::memory_order_seq_cst); + memcpy(&new_sites[0], &sites[0], sizeof(VLogSite *) * (cut_1 + 1)); + memcpy(&new_sites[cut_1 + 1], &sites[cut_2 + 1], + sizeof(VLogSite *) * (cut_3 - cut_2)); + memcpy(&new_sites[cut_1 + 1 + cut_3 - cut_2], &sites[cut_1 + 1], + sizeof(VLogSite *) * (cut_2 - cut_1)); + memcpy(&new_sites[cut_3 + 1], &sites[cut_3 + 1], + sizeof(VLogSite *) * (num_sites_ - cut_3 - 1)); + sites.swap(new_sites); + } + const char *last_file = nullptr; + for (size_t i = 0; i < num_sites_; i++) { + if (sites[i]->file_ == last_file) continue; + last_file = sites[i]->file_; + num_new_files_++; + } + absl::log_internal::SetVModuleListHeadForTestOnly(sites[0]); + sites[num_tus - 1]->next_.store(nullptr, std::memory_order_seq_cst); + } + ~SyntheticBinary() { + static_assert(std::is_trivially_destructible<VLogSite>::value, ""); + absl::log_internal::SetVModuleListHeadForTestOnly(nullptr); + } + + size_t num_sites() const { return num_sites_; } + size_t num_new_files() const { return num_new_files_; } + size_t num_extra_static_data_bytes() const { + return num_extra_static_data_bytes_; + } + + private: + struct PerTU { + container_internal::Layout<char, VLogSite, char> layout; + std::unique_ptr<char[]> buf; + }; + + std::mt19937 bitgen_; + std::vector<PerTU> per_tu_data_; + size_t num_sites_ = 0; + size_t num_new_files_ = 0; + size_t num_extra_static_data_bytes_ = 0; +}; + +namespace { +void BM_UpdateVModuleEmpty(benchmark::State& state) { + SyntheticBinary bin(static_cast<size_t>(state.range(0)), 10 * 1024 * 1024, + 256, state.range(1)); + for (auto s : state) { + absl::log_internal::UpdateVModule(""); + } + state.SetItemsProcessed(static_cast<int>(bin.num_new_files())); +} +BENCHMARK(BM_UpdateVModuleEmpty) + ->ArgPair(100, 200) + ->ArgPair(1000, 2000) + ->ArgPair(10000, 20000); + +void BM_UpdateVModuleShort(benchmark::State& state) { + SyntheticBinary bin(static_cast<size_t>(state.range(0)), 10 * 1024 * 1024, + 256, state.range(1)); + for (auto s : state) { + absl::log_internal::UpdateVModule("directory2/*=4"); + } + state.SetItemsProcessed(static_cast<int>(bin.num_new_files())); +} +BENCHMARK(BM_UpdateVModuleShort) + ->ArgPair(100, 200) + ->ArgPair(1000, 2000) + ->ArgPair(10000, 20000); + +void BM_UpdateVModuleLong(benchmark::State& state) { + SyntheticBinary bin(static_cast<size_t>(state.range(0)), 10 * 1024 * 1024, + 256, state.range(1)); + for (auto s : state) { + absl::log_internal::UpdateVModule( + "d?r?c?o?y2/*=4,d?*r?*c?**o?*y1/*=2,d?*rc?**o?*y3/*=2,," + "d?*r?*c?**o?*1/*=1,d?*r?**o?*y1/*=2,d?*r???***y1/*=7," + "d?*r?**o?*y1/aaa=6"); + } + state.SetItemsProcessed(static_cast<int>(bin.num_new_files())); +} +BENCHMARK(BM_UpdateVModuleLong) + ->ArgPair(100, 200) + ->ArgPair(1000, 2000) + ->ArgPair(10000, 20000); +} // namespace +} // namespace log_internal +ABSL_NAMESPACE_END +} // namespace absl
diff --git a/absl/log/log.h b/absl/log/log.h index 99e78ea..b721b08 100644 --- a/absl/log/log.h +++ b/absl/log/log.h
@@ -53,6 +53,14 @@ // * .NoPrefix() // Omits the prefix from this line. The prefix includes metadata about the // logged data such as source code location and timestamp. +// * .WithVerbosity(int verbose_level) +// Sets the verbosity field of the logged message as if it was logged by +// `VLOG(verbose_level)`. Unlike `VLOG`, this method does not affect +// evaluation of the statement when the specified `verbose_level` has been +// disabled. The only effect is on `LogSink` implementations which make use +// of the `absl::LogSink::verbosity()` value. The value +// `absl::LogEntry::kNoVerbosityLevel` can be specified to mark the message +// not verbose. // * .WithTimestamp(absl::Time timestamp) // Uses the specified timestamp instead of one collected at the time of // execution. @@ -190,6 +198,7 @@ #define ABSL_LOG_LOG_H_ #include "absl/log/internal/log_impl.h" +#include "absl/log/vlog_is_on.h" // IWYU pragma: export // LOG() // @@ -213,11 +222,32 @@ // terminate the program if `NDEBUG` is defined. #define DLOG(severity) ABSL_LOG_INTERNAL_DLOG_IMPL(_##severity) +// `VLOG` uses numeric levels to provide verbose logging that can configured at +// runtime, including at a per-module level. `VLOG` statements are logged at +// `INFO` severity if they are logged at all; the numeric levels are on a +// different scale than the proper severity levels. Positive levels are +// disabled by default. Negative levels should not be used. +// Example: +// +// VLOG(1) << "I print when you run the program with --v=1 or higher"; +// VLOG(2) << "I print when you run the program with --v=2 or higher"; +// +// See vlog_is_on.h for further documentation, including the usage of the +// --vmodule flag to log at different levels in different source files. +#define VLOG(severity) ABSL_LOG_INTERNAL_VLOG_IMPL(severity) + +// `DVLOG` behaves like `VLOG` in debug mode (i.e. `#ifndef NDEBUG`). +// Otherwise, it compiles away and does nothing. +#define DVLOG(severity) ABSL_LOG_INTERNAL_DVLOG_IMPL(severity) + // `LOG_IF` and friends add a second argument which specifies a condition. If // the condition is false, nothing is logged. // Example: // // LOG_IF(INFO, num_cookies > 10) << "Got lots of cookies"; +// +// There is no `VLOG_IF` because the order of evaluation of the arguments is +// ambiguous and the alternate spelling with an `if`-statement is trivial. #define LOG_IF(severity, condition) \ ABSL_LOG_INTERNAL_LOG_IF_IMPL(_##severity, condition) #define PLOG_IF(severity, condition) \ @@ -285,6 +315,15 @@ #define DLOG_EVERY_N_SEC(severity, n_seconds) \ ABSL_LOG_INTERNAL_DLOG_EVERY_N_SEC_IMPL(_##severity, n_seconds) +#define VLOG_EVERY_N(severity, n) \ + ABSL_LOG_INTERNAL_VLOG_EVERY_N_IMPL(severity, n) +#define VLOG_FIRST_N(severity, n) \ + ABSL_LOG_INTERNAL_VLOG_FIRST_N_IMPL(severity, n) +#define VLOG_EVERY_POW_2(severity) \ + ABSL_LOG_INTERNAL_VLOG_EVERY_POW_2_IMPL(severity) +#define VLOG_EVERY_N_SEC(severity, n_seconds) \ + ABSL_LOG_INTERNAL_VLOG_EVERY_N_SEC_IMPL(severity, n_seconds) + // `LOG_IF_EVERY_N` and friends behave as the corresponding `LOG_EVERY_N` // but neither increment a counter nor log a message if condition is false (as // `LOG_IF`).
diff --git a/absl/log/log_entry.h b/absl/log/log_entry.h index 9e4ae8e..7a55dfe 100644 --- a/absl/log/log_entry.h +++ b/absl/log/log_entry.h
@@ -96,7 +96,8 @@ // LogEntry::verbosity() // // Returns this entry's verbosity, or `kNoVerbosityLevel` for a non-verbose - // entry. Verbosity control is not available outside of Google yet. + // entry. Taken from the argument to `VLOG` or from + // `LOG(...).WithVerbosity(...)`. int verbosity() const { return verbose_level_; } // LogEntry::timestamp()
diff --git a/absl/log/stripping_test.cc b/absl/log/stripping_test.cc index 3535703..271fae1 100644 --- a/absl/log/stripping_test.cc +++ b/absl/log/stripping_test.cc
@@ -54,6 +54,7 @@ #include "absl/log/check.h" #include "absl/log/internal/test_helpers.h" #include "absl/log/log.h" +#include "absl/status/status.h" #include "absl/strings/escaping.h" #include "absl/strings/str_format.h" #include "absl/strings/string_view.h" @@ -414,4 +415,88 @@ } } +TEST_F(StrippingTest, CheckOp) { + // See `StrippingTest.Check` for some hairy implementation notes. + const std::string var_needle1 = + absl::Base64Escape("StrippingTestCheckOpVar1"); + const std::string var_needle2 = + absl::Base64Escape("StrippingTestCheckOpVar2"); + const std::string msg_needle = absl::Base64Escape("StrippingTest.CheckOp"); + volatile int U3RyaXBwaW5nVGVzdENoZWNrT3BWYXIx = 0xFEED; + volatile int U3RyaXBwaW5nVGVzdENoZWNrT3BWYXIy = 0xCAFE; + if (kReallyDie) { + CHECK_EQ(U3RyaXBwaW5nVGVzdENoZWNrT3BWYXIx, U3RyaXBwaW5nVGVzdENoZWNrT3BWYXIy) + << "U3RyaXBwaW5nVGVzdC5DaGVja09w"; + } + + std::unique_ptr<FILE, std::function<void(FILE*)>> exe = OpenTestExecutable(); + ASSERT_THAT(exe, NotNull()); + + if (absl::LogSeverity::kFatal >= kAbslMinLogLevel) { + EXPECT_THAT(exe.get(), FileHasSubstr(var_needle1)); + EXPECT_THAT(exe.get(), FileHasSubstr(var_needle2)); + EXPECT_THAT(exe.get(), FileHasSubstr(msg_needle)); + } else { + EXPECT_THAT(exe.get(), Not(FileHasSubstr(var_needle1))); + EXPECT_THAT(exe.get(), Not(FileHasSubstr(var_needle2))); + EXPECT_THAT(exe.get(), Not(FileHasSubstr(msg_needle))); + } +} + +TEST_F(StrippingTest, CheckStrOp) { + // See `StrippingTest.Check` for some hairy implementation notes. + const std::string var_needle1 = + absl::Base64Escape("StrippingTestCheckStrOpVar1"); + const std::string var_needle2 = + absl::Base64Escape("StrippingTestCheckStrOpVar2"); + const std::string msg_needle = absl::Base64Escape("StrippingTest.CheckStrOp"); + const char *volatile U3RyaXBwaW5nVGVzdENoZWNrU3RyT3BWYXIx = "FEED"; + const char *volatile U3RyaXBwaW5nVGVzdENoZWNrU3RyT3BWYXIy = "CAFE"; + if (kReallyDie) { + CHECK_STREQ(U3RyaXBwaW5nVGVzdENoZWNrU3RyT3BWYXIx, + U3RyaXBwaW5nVGVzdENoZWNrU3RyT3BWYXIy) + << "U3RyaXBwaW5nVGVzdC5DaGVja1N0ck9w"; + } + + std::unique_ptr<FILE, std::function<void(FILE*)>> exe = OpenTestExecutable(); + ASSERT_THAT(exe, NotNull()); + + if (absl::LogSeverity::kFatal >= kAbslMinLogLevel) { + EXPECT_THAT(exe.get(), FileHasSubstr(var_needle1)); + EXPECT_THAT(exe.get(), FileHasSubstr(var_needle2)); + EXPECT_THAT(exe.get(), FileHasSubstr(msg_needle)); + } else { + EXPECT_THAT(exe.get(), Not(FileHasSubstr(var_needle1))); + EXPECT_THAT(exe.get(), Not(FileHasSubstr(var_needle2))); + EXPECT_THAT(exe.get(), Not(FileHasSubstr(msg_needle))); + } +} + +TEST_F(StrippingTest, CheckOk) { + // See `StrippingTest.Check` for some hairy implementation notes. + const std::string var_needle = absl::Base64Escape("StrippingTestCheckOkVar1"); + const std::string msg_needle = absl::Base64Escape("StrippingTest.CheckOk"); + volatile bool x = false; + auto U3RyaXBwaW5nVGVzdENoZWNrT2tWYXIx = absl::OkStatus(); + if (x) { + U3RyaXBwaW5nVGVzdENoZWNrT2tWYXIx = + absl::InvalidArgumentError("Stripping this is not my job!"); + } + if (kReallyDie) { + CHECK_OK(U3RyaXBwaW5nVGVzdENoZWNrT2tWYXIx) + << "U3RyaXBwaW5nVGVzdC5DaGVja09r"; + } + + std::unique_ptr<FILE, std::function<void(FILE*)>> exe = OpenTestExecutable(); + ASSERT_THAT(exe, NotNull()); + + if (absl::LogSeverity::kFatal >= kAbslMinLogLevel) { + EXPECT_THAT(exe.get(), FileHasSubstr(var_needle)); + EXPECT_THAT(exe.get(), FileHasSubstr(msg_needle)); + } else { + EXPECT_THAT(exe.get(), Not(FileHasSubstr(var_needle))); + EXPECT_THAT(exe.get(), Not(FileHasSubstr(msg_needle))); + } +} + } // namespace
diff --git a/absl/log/vlog_is_on.h b/absl/log/vlog_is_on.h new file mode 100644 index 0000000..f5c4247 --- /dev/null +++ b/absl/log/vlog_is_on.h
@@ -0,0 +1,70 @@ +// Copyright 2022 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// File: log/vlog_is_on.h +// ----------------------------------------------------------------------------- +// +// This header defines the `VLOG_IS_ON()` macro that controls the +// variable-verbosity conditional logging. +// +// It's used by `VLOG` in log.h, or it can also be used directly like this: +// +// if (VLOG_IS_ON(2)) { +// foo_server.RecomputeStatisticsExpensive(); +// LOG(INFO) << foo_server.LastStatisticsAsString(); +// } +// +// Each source file has an effective verbosity level that's a non-negative +// integer computed from the `--vmodule` and `--v` flags. +// `VLOG_IS_ON(n)` is true, and `VLOG(n)` logs, if that effective verbosity +// level is greater than or equal to `n`. +// +// `--vmodule` takes a comma-delimited list of key=value pairs. Each key is a +// pattern matched against filenames, and the values give the effective severity +// level applied to matching files. '?' and '*' characters in patterns are +// interpreted as single-character and zero-or-more-character wildcards. +// Patterns including a slash character are matched against full pathnames, +// while those without are matched against basenames only. One suffix (i.e. the +// last . and everything after it) is stripped from each filename prior to +// matching, as is the special suffix "-inl". +// +// Files are matched against globs in `--vmodule` in order, and the first match +// determines the verbosity level. +// +// Files which do not match any pattern in `--vmodule` use the value of `--v` as +// their effective verbosity level. The default is 0. +// +// SetVLOGLevel helper function is provided to do limited dynamic control over +// V-logging by appending to `--vmodule`. Because these go at the beginning of +// the list, they take priority over any globs previously added. +// +// Resetting --vmodule will override all previous modifications to `--vmodule`, +// including via SetVLOGLevel. + +#ifndef ABSL_LOG_VLOG_IS_ON_H_ +#define ABSL_LOG_VLOG_IS_ON_H_ + +#include "absl/log/absl_vlog_is_on.h" // IWYU pragma: export + +// Each VLOG_IS_ON call site gets its own VLogSite that registers with the +// global linked list of sites to asynchronously update its verbosity level on +// changes to --v or --vmodule. The verbosity can also be set by manually +// calling SetVLOGLevel. +// +// VLOG_IS_ON is not async signal safe, but it is guaranteed not to allocate +// new memory. +#define VLOG_IS_ON(verbose_level) ABSL_VLOG_IS_ON(verbose_level) + +#endif // ABSL_LOG_VLOG_IS_ON_H_
diff --git a/absl/log/vlog_is_on_test.cc b/absl/log/vlog_is_on_test.cc new file mode 100644 index 0000000..28a2eea --- /dev/null +++ b/absl/log/vlog_is_on_test.cc
@@ -0,0 +1,175 @@ +// Copyright 2023 The Abseil Authors +// +// Licensed under the Apache License, Version 2.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/log/vlog_is_on.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/log_severity.h" +#include "absl/flags/flag.h" +#include "absl/log/flags.h" +#include "absl/log/log.h" +#include "absl/log/scoped_mock_log.h" +#include "absl/types/optional.h" + +namespace { + +using ::testing::_; + +absl::optional<int> MaxLogVerbosity() { +#ifdef ABSL_MAX_VLOG_VERBOSITY + return ABSL_MAX_VLOG_VERBOSITY; +#else + return absl::nullopt; +#endif +} + +absl::optional<int> MinLogLevel() { +#ifdef ABSL_MIN_LOG_LEVEL + return static_cast<int>(ABSL_MIN_LOG_LEVEL); +#else + return absl::nullopt; +#endif +} + +TEST(VLogIsOn, GlobalWorksWithoutMaxVerbosityAndMinLogLevel) { + if (MaxLogVerbosity().has_value() || MinLogLevel().has_value()) { + GTEST_SKIP(); + } + + absl::SetGlobalVLogLevel(3); + absl::ScopedMockLog log(absl::MockLogDefault::kDisallowUnexpected); + + EXPECT_CALL(log, Log(absl::LogSeverity::kInfo, _, "important")); + + log.StartCapturingLogs(); + VLOG(3) << "important"; + VLOG(4) << "spam"; +} + +TEST(VLogIsOn, FileWorksWithoutMaxVerbosityAndMinLogLevel) { + if (MaxLogVerbosity().has_value() || MinLogLevel().has_value()) { + GTEST_SKIP(); + } + + absl::SetVLogLevel("vlog_is_on_test", 3); + absl::ScopedMockLog log(absl::MockLogDefault::kDisallowUnexpected); + + EXPECT_CALL(log, Log(absl::LogSeverity::kInfo, _, "important")); + + log.StartCapturingLogs(); + VLOG(3) << "important"; + VLOG(4) << "spam"; +} + +TEST(VLogIsOn, PatternWorksWithoutMaxVerbosityAndMinLogLevel) { + if (MaxLogVerbosity().has_value() || MinLogLevel().has_value()) { + GTEST_SKIP(); + } + + absl::SetVLogLevel("vlog_is_on*", 3); + absl::ScopedMockLog log(absl::MockLogDefault::kDisallowUnexpected); + + EXPECT_CALL(log, Log(absl::LogSeverity::kInfo, _, "important")); + + log.StartCapturingLogs(); + VLOG(3) << "important"; + VLOG(4) << "spam"; +} + +TEST(VLogIsOn, GlobalDoesNotFilterBelowMaxVerbosity) { + if (!MaxLogVerbosity().has_value() || *MaxLogVerbosity() < 2) { + GTEST_SKIP(); + } + + // Set an arbitrary high value to avoid filtering VLOGs in tests by default. + absl::SetGlobalVLogLevel(1000); + absl::ScopedMockLog log(absl::MockLogDefault::kDisallowUnexpected); + + EXPECT_CALL(log, Log(absl::LogSeverity::kInfo, _, "asdf")); + + log.StartCapturingLogs(); + VLOG(2) << "asdf"; +} + +TEST(VLogIsOn, FileDoesNotFilterBelowMaxVerbosity) { + if (!MaxLogVerbosity().has_value() || *MaxLogVerbosity() < 2) { + GTEST_SKIP(); + } + + // Set an arbitrary high value to avoid filtering VLOGs in tests by default. + absl::SetVLogLevel("vlog_is_on_test", 1000); + absl::ScopedMockLog log(absl::MockLogDefault::kDisallowUnexpected); + + EXPECT_CALL(log, Log(absl::LogSeverity::kInfo, _, "asdf")); + + log.StartCapturingLogs(); + VLOG(2) << "asdf"; +} + +TEST(VLogIsOn, PatternDoesNotFilterBelowMaxVerbosity) { + if (!MaxLogVerbosity().has_value() || *MaxLogVerbosity() < 2) { + GTEST_SKIP(); + } + + // Set an arbitrary high value to avoid filtering VLOGs in tests by default. + absl::SetVLogLevel("vlog_is_on*", 1000); + absl::ScopedMockLog log(absl::MockLogDefault::kDisallowUnexpected); + + EXPECT_CALL(log, Log(absl::LogSeverity::kInfo, _, "asdf")); + + log.StartCapturingLogs(); + VLOG(2) << "asdf"; +} + +TEST(VLogIsOn, GlobalFiltersAboveMaxVerbosity) { + if (!MaxLogVerbosity().has_value() || *MaxLogVerbosity() >= 4) { + GTEST_SKIP(); + } + + // Set an arbitrary high value to avoid filtering VLOGs in tests by default. + absl::SetGlobalVLogLevel(1000); + absl::ScopedMockLog log(absl::MockLogDefault::kDisallowUnexpected); + + log.StartCapturingLogs(); + VLOG(4) << "dfgh"; +} + +TEST(VLogIsOn, FileFiltersAboveMaxVerbosity) { + if (!MaxLogVerbosity().has_value() || *MaxLogVerbosity() >= 4) { + GTEST_SKIP(); + } + + // Set an arbitrary high value to avoid filtering VLOGs in tests by default. + absl::SetVLogLevel("vlog_is_on_test", 1000); + absl::ScopedMockLog log(absl::MockLogDefault::kDisallowUnexpected); + + log.StartCapturingLogs(); + VLOG(4) << "dfgh"; +} + +TEST(VLogIsOn, PatternFiltersAboveMaxVerbosity) { + if (!MaxLogVerbosity().has_value() || *MaxLogVerbosity() >= 4) { + GTEST_SKIP(); + } + + // Set an arbitrary high value to avoid filtering VLOGs in tests by default. + absl::SetVLogLevel("vlog_is_on*", 1000); + absl::ScopedMockLog log(absl::MockLogDefault::kDisallowUnexpected); + + log.StartCapturingLogs(); + VLOG(4) << "dfgh"; +} + +} // namespace
diff --git a/absl/status/BUILD.bazel b/absl/status/BUILD.bazel index 3cf0a5e..a5fad4d 100644 --- a/absl/status/BUILD.bazel +++ b/absl/status/BUILD.bazel
@@ -54,6 +54,7 @@ "//absl/base:config", "//absl/base:core_headers", "//absl/base:no_destructor", + "//absl/base:nullability", "//absl/base:raw_logging_internal", "//absl/base:strerror", "//absl/container:inlined_vector", @@ -100,6 +101,7 @@ "//absl/base", "//absl/base:config", "//absl/base:core_headers", + "//absl/base:nullability", "//absl/base:raw_logging_internal", "//absl/meta:type_traits", "//absl/strings",
diff --git a/absl/status/CMakeLists.txt b/absl/status/CMakeLists.txt index da78990..d9d1683 100644 --- a/absl/status/CMakeLists.txt +++ b/absl/status/CMakeLists.txt
@@ -37,6 +37,7 @@ absl::inlined_vector absl::memory absl::no_destructor + absl::nullability absl::optional absl::raw_logging_internal absl::span @@ -76,6 +77,7 @@ absl::base absl::config absl::core_headers + absl::nullability absl::raw_logging_internal absl::status absl::strings
diff --git a/absl/status/internal/status_internal.cc b/absl/status/internal/status_internal.cc index 2307579..a915675 100644 --- a/absl/status/internal/status_internal.cc +++ b/absl/status/internal/status_internal.cc
@@ -27,6 +27,7 @@ #include "absl/base/attributes.h" #include "absl/base/config.h" #include "absl/base/macros.h" +#include "absl/base/nullability.h" #include "absl/debugging/stacktrace.h" #include "absl/debugging/symbolize.h" #include "absl/memory/memory.h" @@ -187,7 +188,7 @@ return true; } -StatusRep* StatusRep::CloneAndUnref() const { +absl::Nonnull<StatusRep*> StatusRep::CloneAndUnref() const { // Optimization: no need to create a clone if we already have a refcount of 1. if (ref_.load(std::memory_order_acquire) == 1) { // All StatusRep instances are heap allocated and mutable, therefore this @@ -233,8 +234,9 @@ } } -std::string* MakeCheckFailString(const absl::Status* status, - const char* prefix) { +absl::Nonnull<std::string*> MakeCheckFailString( + absl::Nonnull<const absl::Status*> status, + absl::Nonnull<const char*> prefix) { return new std::string( absl::StrCat(prefix, " (", status->ToString(StatusToStringMode::kWithEverything), ")"));
diff --git a/absl/status/internal/status_internal.h b/absl/status/internal/status_internal.h index 7655709..c9f4383 100644 --- a/absl/status/internal/status_internal.h +++ b/absl/status/internal/status_internal.h
@@ -22,6 +22,7 @@ #include "absl/base/attributes.h" #include "absl/base/config.h" +#include "absl/base/nullability.h" #include "absl/container/inlined_vector.h" #include "absl/strings/cord.h" #include "absl/strings/string_view.h" @@ -99,7 +100,7 @@ // Returns an equivalent heap allocated StatusRep with refcount 1. // // `this` is not safe to be used after calling as it may have been deleted. - StatusRep* CloneAndUnref() const; + absl::Nonnull<StatusRep*> CloneAndUnref() const; private: mutable std::atomic<int32_t> ref_; @@ -118,8 +119,10 @@ // suitable for output as an error message in assertion/`CHECK()` failures. // // This is an internal implementation detail for Abseil logging. -std::string* MakeCheckFailString(const absl::Status* status, - const char* prefix); +ABSL_ATTRIBUTE_PURE_FUNCTION +absl::Nonnull<std::string*> MakeCheckFailString( + absl::Nonnull<const absl::Status*> status, + absl::Nonnull<const char*> prefix); } // namespace status_internal
diff --git a/absl/status/internal/statusor_internal.h b/absl/status/internal/statusor_internal.h index 49cead7..25c1147 100644 --- a/absl/status/internal/statusor_internal.h +++ b/absl/status/internal/statusor_internal.h
@@ -18,6 +18,7 @@ #include <utility> #include "absl/base/attributes.h" +#include "absl/base/nullability.h" #include "absl/meta/type_traits.h" #include "absl/status/status.h" #include "absl/utility/utility.h" @@ -123,7 +124,7 @@ class Helper { public: // Move type-agnostic error handling to the .cc. - static void HandleInvalidStatusCtorArg(Status*); + static void HandleInvalidStatusCtorArg(absl::Nonnull<Status*>); ABSL_ATTRIBUTE_NORETURN static void Crash(const absl::Status& status); }; @@ -131,7 +132,8 @@ // the constructor. // This abstraction is here mostly for the gcc performance fix. template <typename T, typename... Args> -ABSL_ATTRIBUTE_NONNULL(1) void PlacementNew(void* p, Args&&... args) { +ABSL_ATTRIBUTE_NONNULL(1) +void PlacementNew(absl::Nonnull<void*> p, Args&&... args) { new (p) T(std::forward<Args>(args)...); }
diff --git a/absl/status/status.cc b/absl/status/status.cc index 7c778ac..4dd5ae0 100644 --- a/absl/status/status.cc +++ b/absl/status/status.cc
@@ -29,6 +29,7 @@ #include "absl/base/internal/strerror.h" #include "absl/base/macros.h" #include "absl/base/no_destructor.h" +#include "absl/base/nullability.h" #include "absl/debugging/stacktrace.h" #include "absl/debugging/symbolize.h" #include "absl/status/internal/status_internal.h" @@ -90,7 +91,7 @@ return os << StatusCodeToString(code); } -const std::string* Status::EmptyString() { +absl::Nonnull<const std::string*> Status::EmptyString() { static const absl::NoDestructor<std::string> kEmpty; return kEmpty.get(); } @@ -99,7 +100,7 @@ constexpr const char Status::kMovedFromString[]; #endif -const std::string* Status::MovedFromString() { +absl::Nonnull<const std::string*> Status::MovedFromString() { static const absl::NoDestructor<std::string> kMovedFrom(kMovedFromString); return kMovedFrom.get(); } @@ -111,7 +112,8 @@ } } -status_internal::StatusRep* Status::PrepareToModify(uintptr_t rep) { +absl::Nonnull<status_internal::StatusRep*> Status::PrepareToModify( + uintptr_t rep) { if (IsInlined(rep)) { return new status_internal::StatusRep(InlinedRepToCode(rep), absl::string_view(), nullptr); @@ -412,7 +414,7 @@ MessageForErrnoToStatus(error_number, message)); } -const char* StatusMessageAsCStr(const Status& status) { +absl::Nonnull<const char*> StatusMessageAsCStr(const Status& status) { // As an internal implementation detail, we guarantee that if status.message() // is non-empty, then the resulting string_view is null terminated. auto sv_message = status.message();
diff --git a/absl/status/status.h b/absl/status/status.h index d4f7414..9ce16db 100644 --- a/absl/status/status.h +++ b/absl/status/status.h
@@ -60,6 +60,7 @@ #include "absl/base/attributes.h" #include "absl/base/config.h" #include "absl/base/macros.h" +#include "absl/base/nullability.h" #include "absl/base/optimization.h" #include "absl/functional/function_ref.h" #include "absl/status/internal/status_internal.h" @@ -622,14 +623,15 @@ // REQUIRES: !ok() // Ensures rep is not inlined or shared with any other Status. - static status_internal::StatusRep* PrepareToModify(uintptr_t rep); + static absl::Nonnull<status_internal::StatusRep*> PrepareToModify( + uintptr_t rep); // MSVC 14.0 limitation requires the const. static constexpr const char kMovedFromString[] = "Status accessed after move."; - static const std::string* EmptyString(); - static const std::string* MovedFromString(); + static absl::Nonnull<const std::string*> EmptyString(); + static absl::Nonnull<const std::string*> MovedFromString(); // Returns whether rep contains an inlined representation. // See rep_ for details. @@ -648,7 +650,8 @@ // Converts between StatusRep* and the external uintptr_t representation used // by rep_. See rep_ for details. static uintptr_t PointerToRep(status_internal::StatusRep* r); - static const status_internal::StatusRep* RepToPointer(uintptr_t r); + static absl::Nonnull<const status_internal::StatusRep*> RepToPointer( + uintptr_t r); static std::string ToStringSlow(uintptr_t rep, StatusToStringMode mode); @@ -899,12 +902,14 @@ return CodeToInlinedRep(absl::StatusCode::kInternal) | 2; } -inline const status_internal::StatusRep* Status::RepToPointer(uintptr_t rep) { +inline absl::Nonnull<const status_internal::StatusRep*> Status::RepToPointer( + uintptr_t rep) { assert(!IsInlined(rep)); return reinterpret_cast<const status_internal::StatusRep*>(rep); } -inline uintptr_t Status::PointerToRep(status_internal::StatusRep* rep) { +inline uintptr_t Status::PointerToRep( + absl::Nonnull<status_internal::StatusRep*> rep) { return reinterpret_cast<uintptr_t>(rep); } @@ -929,7 +934,7 @@ // If the status's message is empty, the empty string is returned. // // StatusMessageAsCStr exists for C support. Use `status.message()` in C++. -const char* StatusMessageAsCStr( +absl::Nonnull<const char*> StatusMessageAsCStr( const Status& status ABSL_ATTRIBUTE_LIFETIME_BOUND); ABSL_NAMESPACE_END
diff --git a/absl/status/status_payload_printer.h b/absl/status/status_payload_printer.h index 5e0937f..f22255e 100644 --- a/absl/status/status_payload_printer.h +++ b/absl/status/status_payload_printer.h
@@ -16,6 +16,7 @@ #include <string> +#include "absl/base/nullability.h" #include "absl/strings/cord.h" #include "absl/strings/string_view.h" #include "absl/types/optional.h" @@ -34,8 +35,8 @@ // NOTE: This is an internal API and the design is subject to change in the // future in a non-backward-compatible way. Since it's only meant for debugging // purpose, you should not rely on it in any critical logic. -using StatusPayloadPrinter = absl::optional<std::string> (*)(absl::string_view, - const absl::Cord&); +using StatusPayloadPrinter = absl::Nullable<absl::optional<std::string> (*)( + absl::string_view, const absl::Cord&)>; // Sets the global payload printer. Only one printer should be set per process. // If multiple printers are set, it's undefined which one will be used.
diff --git a/absl/status/statusor.cc b/absl/status/statusor.cc index bfad75e..7e6b334 100644 --- a/absl/status/statusor.cc +++ b/absl/status/statusor.cc
@@ -19,6 +19,7 @@ #include "absl/base/call_once.h" #include "absl/base/config.h" #include "absl/base/internal/raw_logging.h" +#include "absl/base/nullability.h" #include "absl/status/internal/statusor_internal.h" #include "absl/status/status.h" #include "absl/strings/str_cat.h" @@ -54,7 +55,7 @@ BadStatusOrAccess::BadStatusOrAccess(BadStatusOrAccess&& other) : status_(std::move(other.status_)) {} -const char* BadStatusOrAccess::what() const noexcept { +absl::Nonnull<const char*> BadStatusOrAccess::what() const noexcept { InitWhat(); return what_.c_str(); } @@ -69,7 +70,7 @@ namespace internal_statusor { -void Helper::HandleInvalidStatusCtorArg(absl::Status* status) { +void Helper::HandleInvalidStatusCtorArg(absl::Nonnull<absl::Status*> status) { const char* kMessage = "An OK status is not a valid constructor argument to StatusOr<T>"; #ifdef NDEBUG
diff --git a/absl/status/statusor.h b/absl/status/statusor.h index 54c7ce0..33a2cf3 100644 --- a/absl/status/statusor.h +++ b/absl/status/statusor.h
@@ -44,6 +44,7 @@ #include <utility> #include "absl/base/attributes.h" +#include "absl/base/nullability.h" #include "absl/base/call_once.h" #include "absl/meta/type_traits.h" #include "absl/status/internal/statusor_internal.h" @@ -88,7 +89,7 @@ // // The pointer of this string is guaranteed to be valid until any non-const // function is invoked on the exception object. - const char* what() const noexcept override; + absl::Nonnull<const char*> what() const noexcept override; // BadStatusOrAccess::status() // @@ -750,13 +751,13 @@ } template <typename T> -const T* StatusOr<T>::operator->() const { +absl::Nonnull<const T*> StatusOr<T>::operator->() const { this->EnsureOk(); return &this->data_; } template <typename T> -T* StatusOr<T>::operator->() { +absl::Nonnull<T*> StatusOr<T>::operator->() { this->EnsureOk(); return &this->data_; }
diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel index a3ef3ae..3a244b5 100644 --- a/absl/strings/BUILD.bazel +++ b/absl/strings/BUILD.bazel
@@ -41,6 +41,7 @@ "//absl/base", "//absl/base:config", "//absl/base:core_headers", + "//absl/base:nullability", "//absl/base:throw_delegate", ], ) @@ -103,6 +104,7 @@ "//absl/base:config", "//absl/base:core_headers", "//absl/base:endian", + "//absl/base:nullability", "//absl/base:raw_logging_internal", "//absl/base:throw_delegate", "//absl/memory", @@ -588,6 +590,7 @@ "//absl/base:config", "//absl/base:core_headers", "//absl/base:endian", + "//absl/base:nullability", "//absl/base:raw_logging_internal", "//absl/container:inlined_vector", "//absl/crc:crc32c", @@ -869,6 +872,7 @@ ":strings", "//absl/base:config", "//absl/base:core_headers", + "//absl/base:nullability", "@com_google_googletest//:gtest", ], ) @@ -1247,6 +1251,11 @@ linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ ":str_format_internal", + ":string_view", + "//absl/base:config", + "//absl/base:core_headers", + "//absl/base:nullability", + "//absl/types:span", ], ) @@ -1277,6 +1286,7 @@ ":strings", "//absl/base:config", "//absl/base:core_headers", + "//absl/container:fixed_array", "//absl/container:inlined_vector", "//absl/functional:function_ref", "//absl/meta:type_traits", @@ -1330,6 +1340,7 @@ deps = [ ":str_format", ":str_format_internal", + "//absl/base:config", "@com_google_googletest//:gtest", "@com_google_googletest//:gtest_main", ], @@ -1366,12 +1377,16 @@ copts = ABSL_TEST_COPTS, visibility = ["//visibility:private"], deps = [ + ":str_format", ":str_format_internal", ":strings", + "//absl/base:config", "//absl/base:core_headers", "//absl/base:raw_logging_internal", "//absl/log", + "//absl/numeric:int128", "//absl/types:optional", + "//absl/types:span", "@com_google_googletest//:gtest", "@com_google_googletest//:gtest_main", ], @@ -1397,6 +1412,8 @@ visibility = ["//visibility:private"], deps = [ ":str_format_internal", + ":string_view", + "//absl/base:config", "//absl/base:core_headers", "@com_google_googletest//:gtest", "@com_google_googletest//:gtest_main",
diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt index b129096..d4116a8 100644 --- a/absl/strings/CMakeLists.txt +++ b/absl/strings/CMakeLists.txt
@@ -27,6 +27,7 @@ absl::base absl::config absl::core_headers + absl::nullability absl::throw_delegate PUBLIC ) @@ -84,6 +85,7 @@ absl::endian absl::int128 absl::memory + absl::nullability absl::raw_logging_internal absl::throw_delegate absl::type_traits @@ -470,7 +472,12 @@ COPTS ${ABSL_DEFAULT_COPTS} DEPS + absl::config + absl::core_headers + absl::nullability + absl::span absl::str_format_internal + absl::string_view PUBLIC ) @@ -501,6 +508,7 @@ absl::strings absl::config absl::core_headers + absl::fixed_array absl::inlined_vector absl::numeric_representation absl::type_traits @@ -548,6 +556,7 @@ COPTS ${ABSL_TEST_COPTS} DEPS + absl::config absl::str_format absl::str_format_internal GTest::gmock_main @@ -585,12 +594,15 @@ COPTS ${ABSL_TEST_COPTS} DEPS - absl::strings - absl::str_format_internal + absl::config absl::core_headers + absl::int128 absl::log absl::raw_logging_internal - absl::int128 + absl::span + absl::str_format + absl::str_format_internal + absl::strings GTest::gmock_main ) @@ -616,6 +628,8 @@ ${ABSL_TEST_COPTS} DEPS absl::str_format_internal + absl::string_view + absl::config absl::core_headers GTest::gmock_main ) @@ -975,6 +989,7 @@ absl::endian absl::function_ref absl::inlined_vector + absl::nullability absl::optional absl::raw_logging_internal absl::span @@ -1032,6 +1047,7 @@ absl::cordz_statistics absl::cordz_update_tracker absl::core_headers + absl::nullability absl::strings TESTONLY )
diff --git a/absl/strings/ascii.cc b/absl/strings/ascii.cc index 1e6566e..df905e0 100644 --- a/absl/strings/ascii.cc +++ b/absl/strings/ascii.cc
@@ -19,6 +19,7 @@ #include <string> #include "absl/base/config.h" +#include "absl/base/nullability.h" namespace absl { ABSL_NAMESPACE_BEGIN @@ -174,7 +175,8 @@ } template <bool ToUpper> -constexpr void AsciiStrCaseFold(char* p, char* end) { +constexpr void AsciiStrCaseFold(absl::Nonnull<char*> p, + absl::Nonnull<char*> end) { // The upper- and lowercase versions of ASCII characters differ by only 1 bit. // When we need to flip the case, we can xor with this bit to achieve the // desired result. Note that the choice of 'a' and 'A' here is arbitrary. We @@ -215,17 +217,17 @@ } // namespace ascii_internal -void AsciiStrToLower(std::string* s) { +void AsciiStrToLower(absl::Nonnull<std::string*> s) { char* p = &(*s)[0]; // Guaranteed to be valid for empty strings return ascii_internal::AsciiStrCaseFold<false>(p, p + s->size()); } -void AsciiStrToUpper(std::string* s) { +void AsciiStrToUpper(absl::Nonnull<std::string*> s) { char* p = &(*s)[0]; // Guaranteed to be valid for empty strings return ascii_internal::AsciiStrCaseFold<true>(p, p + s->size()); } -void RemoveExtraAsciiWhitespace(std::string* str) { +void RemoveExtraAsciiWhitespace(absl::Nonnull<std::string*> str) { auto stripped = StripAsciiWhitespace(*str); if (stripped.empty()) {
diff --git a/absl/strings/ascii.h b/absl/strings/ascii.h index ba6679b..c238f4d 100644 --- a/absl/strings/ascii.h +++ b/absl/strings/ascii.h
@@ -58,6 +58,7 @@ #include "absl/base/attributes.h" #include "absl/base/config.h" +#include "absl/base/nullability.h" #include "absl/strings/string_view.h" namespace absl { @@ -166,7 +167,7 @@ } // Converts the characters in `s` to lowercase, changing the contents of `s`. -void AsciiStrToLower(std::string* s); +void AsciiStrToLower(absl::Nonnull<std::string*> s); // Creates a lowercase string from a given absl::string_view. ABSL_MUST_USE_RESULT inline std::string AsciiStrToLower(absl::string_view s) { @@ -184,7 +185,7 @@ } // Converts the characters in `s` to uppercase, changing the contents of `s`. -void AsciiStrToUpper(std::string* s); +void AsciiStrToUpper(absl::Nonnull<std::string*> s); // Creates an uppercase string from a given absl::string_view. ABSL_MUST_USE_RESULT inline std::string AsciiStrToUpper(absl::string_view s) { @@ -202,7 +203,7 @@ } // Strips in place whitespace from the beginning of the given string. -inline void StripLeadingAsciiWhitespace(std::string* str) { +inline void StripLeadingAsciiWhitespace(absl::Nonnull<std::string*> str) { auto it = std::find_if_not(str->begin(), str->end(), absl::ascii_isspace); str->erase(str->begin(), it); } @@ -216,7 +217,7 @@ } // Strips in place whitespace from the end of the given string -inline void StripTrailingAsciiWhitespace(std::string* str) { +inline void StripTrailingAsciiWhitespace(absl::Nonnull<std::string*> str) { auto it = std::find_if_not(str->rbegin(), str->rend(), absl::ascii_isspace); str->erase(static_cast<size_t>(str->rend() - it)); } @@ -229,13 +230,13 @@ } // Strips in place whitespace from both ends of the given string -inline void StripAsciiWhitespace(std::string* str) { +inline void StripAsciiWhitespace(absl::Nonnull<std::string*> str) { StripTrailingAsciiWhitespace(str); StripLeadingAsciiWhitespace(str); } // Removes leading, trailing, and consecutive internal whitespace. -void RemoveExtraAsciiWhitespace(std::string*); +void RemoveExtraAsciiWhitespace(absl::Nonnull<std::string*> str); ABSL_NAMESPACE_END } // namespace absl
diff --git a/absl/strings/charconv.cc b/absl/strings/charconv.cc index 60b3715..eda6d50 100644 --- a/absl/strings/charconv.cc +++ b/absl/strings/charconv.cc
@@ -23,6 +23,7 @@ #include "absl/base/casts.h" #include "absl/base/config.h" +#include "absl/base/nullability.h" #include "absl/numeric/bits.h" #include "absl/numeric/int128.h" #include "absl/strings/internal/charconv_bigint.h" @@ -119,7 +120,7 @@ // Parsing a smaller N will produce something finite. static constexpr int kEiselLemireMaxExclusiveExp10 = 309; - static double MakeNan(const char* tagp) { + static double MakeNan(absl::Nullable<const char*> tagp) { #if ABSL_HAVE_BUILTIN(__builtin_nan) // Use __builtin_nan() if available since it has a fix for // https://bugs.llvm.org/show_bug.cgi?id=37778 @@ -192,7 +193,7 @@ static constexpr int kEiselLemireMinInclusiveExp10 = -46 - 18; static constexpr int kEiselLemireMaxExclusiveExp10 = 39; - static float MakeNan(const char* tagp) { + static float MakeNan(absl::Nullable<const char*> tagp) { #if ABSL_HAVE_BUILTIN(__builtin_nanf) // Use __builtin_nanf() if available since it has a fix for // https://bugs.llvm.org/show_bug.cgi?id=37778 @@ -344,7 +345,7 @@ // `value` must be wider than the requested bit width. // // Returns the number of bits shifted. -int TruncateToBitWidth(int bit_width, uint128* value) { +int TruncateToBitWidth(int bit_width, absl::Nonnull<uint128*> value) { const int current_bit_width = BitWidth(*value); const int shift = current_bit_width - bit_width; *value >>= shift; @@ -356,7 +357,7 @@ // the appropriate double, and returns true. template <typename FloatType> bool HandleEdgeCase(const strings_internal::ParsedFloat& input, bool negative, - FloatType* value) { + absl::Nonnull<FloatType*> value) { if (input.type == strings_internal::FloatType::kNan) { // A bug in both clang < 7 and gcc would cause the compiler to optimize // away the buffer we are building below. Declaring the buffer volatile @@ -405,7 +406,8 @@ // number is stored in *value. template <typename FloatType> void EncodeResult(const CalculatedFloat& calculated, bool negative, - absl::from_chars_result* result, FloatType* value) { + absl::Nonnull<absl::from_chars_result*> result, + absl::Nonnull<FloatType*> value) { if (calculated.exponent == kOverflow) { result->ec = std::errc::result_out_of_range; *value = negative ? -std::numeric_limits<FloatType>::max() @@ -451,7 +453,7 @@ // Zero and negative values of `shift` are accepted, in which case the word is // shifted left, as necessary. uint64_t ShiftRightAndRound(uint128 value, int shift, bool input_exact, - bool* output_exact) { + absl::Nonnull<bool*> output_exact) { if (shift <= 0) { *output_exact = input_exact; return static_cast<uint64_t>(value << -shift); @@ -685,7 +687,8 @@ // this function returns false) is both fast and correct. template <typename FloatType> bool EiselLemire(const strings_internal::ParsedFloat& input, bool negative, - FloatType* value, std::errc* ec) { + absl::Nonnull<FloatType*> value, + absl::Nonnull<std::errc*> ec) { uint64_t man = input.mantissa; int exp10 = input.exponent; if (exp10 < FloatTraits<FloatType>::kEiselLemireMinInclusiveExp10) { @@ -858,7 +861,8 @@ } template <typename FloatType> -from_chars_result FromCharsImpl(const char* first, const char* last, +from_chars_result FromCharsImpl(absl::Nonnull<const char*> first, + absl::Nonnull<const char*> last, FloatType& value, chars_format fmt_flags) { from_chars_result result; result.ptr = first; // overwritten on successful parse @@ -944,12 +948,14 @@ } } // namespace -from_chars_result from_chars(const char* first, const char* last, double& value, +from_chars_result from_chars(absl::Nonnull<const char*> first, + absl::Nonnull<const char*> last, double& value, chars_format fmt) { return FromCharsImpl(first, last, value, fmt); } -from_chars_result from_chars(const char* first, const char* last, float& value, +from_chars_result from_chars(absl::Nonnull<const char*> first, + absl::Nonnull<const char*> last, float& value, chars_format fmt) { return FromCharsImpl(first, last, value, fmt); }
diff --git a/absl/strings/charconv.h b/absl/strings/charconv.h index 111c712..be25090 100644 --- a/absl/strings/charconv.h +++ b/absl/strings/charconv.h
@@ -18,6 +18,7 @@ #include <system_error> // NOLINT(build/c++11) #include "absl/base/config.h" +#include "absl/base/nullability.h" namespace absl { ABSL_NAMESPACE_BEGIN @@ -44,7 +45,7 @@ // characters that were successfully parsed. If none was found, `ptr` is set // to the `first` argument to from_chars. struct from_chars_result { - const char* ptr; + absl::Nonnull<const char*> ptr; std::errc ec; }; @@ -76,11 +77,13 @@ // format that strtod() accepts, except that a "0x" prefix is NOT matched. // (In particular, in `hex` mode, the input "0xff" results in the largest // matching pattern "0".) -absl::from_chars_result from_chars(const char* first, const char* last, +absl::from_chars_result from_chars(absl::Nonnull<const char*> first, + absl::Nonnull<const char*> last, double& value, // NOLINT chars_format fmt = chars_format::general); -absl::from_chars_result from_chars(const char* first, const char* last, +absl::from_chars_result from_chars(absl::Nonnull<const char*> first, + absl::Nonnull<const char*> last, float& value, // NOLINT chars_format fmt = chars_format::general);
diff --git a/absl/strings/cord.cc b/absl/strings/cord.cc index 08a165e..f67326f 100644 --- a/absl/strings/cord.cc +++ b/absl/strings/cord.cc
@@ -37,6 +37,7 @@ #include "absl/base/internal/raw_logging.h" #include "absl/base/macros.h" #include "absl/base/optimization.h" +#include "absl/base/nullability.h" #include "absl/container/inlined_vector.h" #include "absl/crc/crc32c.h" #include "absl/crc/internal/crc_cord_state.h" @@ -74,18 +75,21 @@ using ::absl::cord_internal::kInlinedVectorSize; using ::absl::cord_internal::kMaxBytesToCopy; -static void DumpNode(CordRep* rep, bool include_data, std::ostream* os, - int indent = 0); -static bool VerifyNode(CordRep* root, CordRep* start_node); +static void DumpNode(absl::Nonnull<CordRep*> rep, bool include_data, + absl::Nonnull<std::ostream*> os, int indent = 0); +static bool VerifyNode(absl::Nonnull<CordRep*> root, + absl::Nonnull<CordRep*> start_node); -static inline CordRep* VerifyTree(CordRep* node) { +static inline absl::Nullable<CordRep*> VerifyTree( + absl::Nullable<CordRep*> node) { assert(node == nullptr || VerifyNode(node, node)); static_cast<void>(&VerifyNode); return node; } -static CordRepFlat* CreateFlat(const char* data, size_t length, - size_t alloc_hint) { +static absl::Nonnull<CordRepFlat*> CreateFlat(absl::Nonnull<const char*> data, + size_t length, + size_t alloc_hint) { CordRepFlat* flat = CordRepFlat::New(length + alloc_hint); flat->length = length; memcpy(flat->Data(), data, length); @@ -94,7 +98,8 @@ // Creates a new flat or Btree out of the specified array. // The returned node has a refcount of 1. -static CordRep* NewBtree(const char* data, size_t length, size_t alloc_hint) { +static absl::Nonnull<CordRep*> NewBtree(absl::Nonnull<const char*> data, + size_t length, size_t alloc_hint) { if (length <= kMaxFlatLength) { return CreateFlat(data, length, alloc_hint); } @@ -107,14 +112,16 @@ // Create a new tree out of the specified array. // The returned node has a refcount of 1. -static CordRep* NewTree(const char* data, size_t length, size_t alloc_hint) { +static absl::Nullable<CordRep*> NewTree(absl::Nullable<const char*> data, + size_t length, size_t alloc_hint) { if (length == 0) return nullptr; return NewBtree(data, length, alloc_hint); } namespace cord_internal { -void InitializeCordRepExternal(absl::string_view data, CordRepExternal* rep) { +void InitializeCordRepExternal(absl::string_view data, + absl::Nonnull<CordRepExternal*> rep) { assert(!data.empty()); rep->length = data.size(); rep->tag = EXTERNAL; @@ -128,7 +135,7 @@ // and not wasteful, we move the string into an external cord rep, preserving // the already allocated string contents. // Requires the provided string length to be larger than `kMaxInline`. -static CordRep* CordRepFromString(std::string&& src) { +static absl::Nonnull<CordRep*> CordRepFromString(std::string&& src) { assert(src.length() > cord_internal::kMaxInline); if ( // String is short: copy data to avoid external block overhead. @@ -160,12 +167,13 @@ constexpr unsigned char Cord::InlineRep::kMaxInline; #endif -inline void Cord::InlineRep::set_data(const char* data, size_t n) { +inline void Cord::InlineRep::set_data(absl::Nonnull<const char*> data, + size_t n) { static_assert(kMaxInline == 15, "set_data is hard-coded for a length of 15"); data_.set_inline_data(data, n); } -inline char* Cord::InlineRep::set_data(size_t n) { +inline absl::Nonnull<char*> Cord::InlineRep::set_data(size_t n) { assert(n <= kMaxInline); ResetToEmpty(); set_inline_size(n); @@ -189,13 +197,13 @@ // Returns `rep` converted into a CordRepBtree. // Directly returns `rep` if `rep` is already a CordRepBtree. -static CordRepBtree* ForceBtree(CordRep* rep) { +static absl::Nonnull<CordRepBtree*> ForceBtree(CordRep* rep) { return rep->IsBtree() ? rep->btree() : CordRepBtree::Create(cord_internal::RemoveCrcNode(rep)); } -void Cord::InlineRep::AppendTreeToInlined(CordRep* tree, +void Cord::InlineRep::AppendTreeToInlined(absl::Nonnull<CordRep*> tree, MethodIdentifier method) { assert(!is_tree()); if (!data_.is_empty()) { @@ -205,14 +213,16 @@ EmplaceTree(tree, method); } -void Cord::InlineRep::AppendTreeToTree(CordRep* tree, MethodIdentifier method) { +void Cord::InlineRep::AppendTreeToTree(absl::Nonnull<CordRep*> tree, + MethodIdentifier method) { assert(is_tree()); const CordzUpdateScope scope(data_.cordz_info(), method); tree = CordRepBtree::Append(ForceBtree(data_.as_tree()), tree); SetTree(tree, scope); } -void Cord::InlineRep::AppendTree(CordRep* tree, MethodIdentifier method) { +void Cord::InlineRep::AppendTree(absl::Nonnull<CordRep*> tree, + MethodIdentifier method) { assert(tree != nullptr); assert(tree->length != 0); assert(!tree->IsCrc()); @@ -223,7 +233,7 @@ } } -void Cord::InlineRep::PrependTreeToInlined(CordRep* tree, +void Cord::InlineRep::PrependTreeToInlined(absl::Nonnull<CordRep*> tree, MethodIdentifier method) { assert(!is_tree()); if (!data_.is_empty()) { @@ -233,7 +243,7 @@ EmplaceTree(tree, method); } -void Cord::InlineRep::PrependTreeToTree(CordRep* tree, +void Cord::InlineRep::PrependTreeToTree(absl::Nonnull<CordRep*> tree, MethodIdentifier method) { assert(is_tree()); const CordzUpdateScope scope(data_.cordz_info(), method); @@ -241,7 +251,8 @@ SetTree(tree, scope); } -void Cord::InlineRep::PrependTree(CordRep* tree, MethodIdentifier method) { +void Cord::InlineRep::PrependTree(absl::Nonnull<CordRep*> tree, + MethodIdentifier method) { assert(tree != nullptr); assert(tree->length != 0); assert(!tree->IsCrc()); @@ -256,8 +267,9 @@ // suitable leaf is found, the function will update the length field for all // nodes to account for the size increase. The append region address will be // written to region and the actual size increase will be written to size. -static inline bool PrepareAppendRegion(CordRep* root, char** region, - size_t* size, size_t max_length) { +static inline bool PrepareAppendRegion( + absl::Nonnull<CordRep*> root, absl::Nonnull<absl::Nullable<char*>*> region, + absl::Nonnull<size_t*> size, size_t max_length) { if (root->IsBtree() && root->refcount.IsOne()) { Span<char> span = root->btree()->GetAppendBuffer(max_length); if (!span.empty()) { @@ -460,11 +472,11 @@ CommitTree(root, rep, scope, method); } -inline CordRep* Cord::TakeRep() const& { +inline absl::Nonnull<CordRep*> Cord::TakeRep() const& { return CordRep::Ref(contents_.tree()); } -inline CordRep* Cord::TakeRep() && { +inline absl::Nonnull<CordRep*> Cord::TakeRep() && { CordRep* rep = contents_.tree(); contents_.clear(); return rep; @@ -522,7 +534,7 @@ contents_.AppendTree(rep, CordzUpdateTracker::kAppendCord); } -static CordRep::ExtractResult ExtractAppendBuffer(CordRep* rep, +static CordRep::ExtractResult ExtractAppendBuffer(absl::Nonnull<CordRep*> rep, size_t min_capacity) { switch (rep->tag) { case cord_internal::BTREE: @@ -769,8 +781,9 @@ return static_cast<int>(memcmp_res > 0) - static_cast<int>(memcmp_res < 0); } -int CompareChunks(absl::string_view* lhs, absl::string_view* rhs, - size_t* size_to_compare) { +int CompareChunks(absl::Nonnull<absl::string_view*> lhs, + absl::Nonnull<absl::string_view*> rhs, + absl::Nonnull<size_t*> size_to_compare) { size_t compared_size = std::min(lhs->size(), rhs->size()); assert(*size_to_compare >= compared_size); *size_to_compare -= compared_size; @@ -868,7 +881,8 @@ SetCrcCordState(std::move(state)); } -const crc_internal::CrcCordState* Cord::MaybeGetCrcCordState() const { +absl::Nullable<const crc_internal::CrcCordState*> Cord::MaybeGetCrcCordState() + const { if (!contents_.is_tree() || !contents_.tree()->IsCrc()) { return nullptr; } @@ -885,7 +899,8 @@ inline int Cord::CompareSlowPath(absl::string_view rhs, size_t compared_size, size_t size_to_compare) const { - auto advance = [](Cord::ChunkIterator* it, absl::string_view* chunk) { + auto advance = [](absl::Nonnull<Cord::ChunkIterator*> it, + absl::Nonnull<absl::string_view*> chunk) { if (!chunk->empty()) return true; ++*it; if (it->bytes_remaining_ == 0) return false; @@ -915,7 +930,8 @@ inline int Cord::CompareSlowPath(const Cord& rhs, size_t compared_size, size_t size_to_compare) const { - auto advance = [](Cord::ChunkIterator* it, absl::string_view* chunk) { + auto advance = [](absl::Nonnull<Cord::ChunkIterator*> it, + absl::Nonnull<absl::string_view*> chunk) { if (!chunk->empty()) return true; ++*it; if (it->bytes_remaining_ == 0) return false; @@ -1037,7 +1053,7 @@ return s; } -void CopyCordToString(const Cord& src, std::string* dst) { +void CopyCordToString(const Cord& src, absl::Nonnull<std::string*> dst) { if (!src.contents_.is_tree()) { src.contents_.CopyTo(dst); } else { @@ -1046,7 +1062,7 @@ } } -void Cord::CopyToArraySlowPath(char* dst) const { +void Cord::CopyToArraySlowPath(absl::Nonnull<char*> dst) const { assert(contents_.is_tree()); absl::string_view fragment; if (GetFlatAux(contents_.tree(), &fragment)) { @@ -1372,7 +1388,8 @@ return absl::string_view(new_buffer, total_size); } -/* static */ bool Cord::GetFlatAux(CordRep* rep, absl::string_view* fragment) { +/* static */ bool Cord::GetFlatAux(absl::Nonnull<CordRep*> rep, + absl::Nonnull<absl::string_view*> fragment) { assert(rep != nullptr); if (rep->length == 0) { *fragment = absl::string_view(); @@ -1406,7 +1423,7 @@ } /* static */ void Cord::ForEachChunkAux( - absl::cord_internal::CordRep* rep, + absl::Nonnull<absl::cord_internal::CordRep*> rep, absl::FunctionRef<void(absl::string_view)> callback) { assert(rep != nullptr); if (rep->length == 0) return; @@ -1431,8 +1448,8 @@ } } -static void DumpNode(CordRep* rep, bool include_data, std::ostream* os, - int indent) { +static void DumpNode(absl::Nonnull<CordRep*> rep, bool include_data, + absl::Nonnull<std::ostream*> os, int indent) { const int kIndentStep = 1; absl::InlinedVector<CordRep*, kInlinedVectorSize> stack; absl::InlinedVector<int, kInlinedVectorSize> indents; @@ -1482,15 +1499,17 @@ ABSL_INTERNAL_CHECK(indents.empty(), ""); } -static std::string ReportError(CordRep* root, CordRep* node) { +static std::string ReportError(absl::Nonnull<CordRep*> root, + absl::Nonnull<CordRep*> node) { std::ostringstream buf; buf << "Error at node " << node << " in:"; DumpNode(root, true, &buf); return buf.str(); } -static bool VerifyNode(CordRep* root, CordRep* start_node) { - absl::InlinedVector<CordRep*, 2> worklist; +static bool VerifyNode(absl::Nonnull<CordRep*> root, + absl::Nonnull<CordRep*> start_node) { + absl::InlinedVector<absl::Nonnull<CordRep*>, 2> worklist; worklist.push_back(start_node); do { CordRep* node = worklist.back();
diff --git a/absl/strings/cord.h b/absl/strings/cord.h index e8bb4f7..b3e556b 100644 --- a/absl/strings/cord.h +++ b/absl/strings/cord.h
@@ -74,6 +74,7 @@ #include "absl/base/internal/endian.h" #include "absl/base/internal/per_thread_tls.h" #include "absl/base/macros.h" +#include "absl/base/nullability.h" #include "absl/base/port.h" #include "absl/container/inlined_vector.h" #include "absl/crc/internal/crc_cord_state.h" @@ -102,7 +103,7 @@ class CordTestPeer; template <typename Releaser> Cord MakeCordFromExternal(absl::string_view, Releaser&&); -void CopyCordToString(const Cord& src, std::string* dst); +void CopyCordToString(const Cord& src, absl::Nonnull<std::string*> dst); // Cord memory accounting modes enum class CordMemoryAccounting { @@ -119,8 +120,8 @@ // // For example: // absl::Cord cord; - // cord.append(some_other_cord); - // cord.append(some_other_cord); + // cord.Append(some_other_cord); + // cord.Append(some_other_cord); // // Counts `some_other_cord` twice: // cord.EstimatedMemoryUsage(kTotal); // // Counts `some_other_cord` once: @@ -416,7 +417,8 @@ // guarantee that pointers previously returned by `dst->data()` remain valid // even if `*dst` had enough capacity to hold `src`. If `*dst` is a new // object, prefer to simply use the conversion operator to `std::string`. - friend void CopyCordToString(const Cord& src, std::string* dst); + friend void CopyCordToString(const Cord& src, + absl::Nonnull<std::string*> dst); class CharIterator; @@ -453,7 +455,7 @@ using iterator_category = std::input_iterator_tag; using value_type = absl::string_view; using difference_type = ptrdiff_t; - using pointer = const value_type*; + using pointer = absl::Nonnull<const value_type*>; using reference = value_type; ChunkIterator() = default; @@ -473,14 +475,14 @@ using CordRepBtree = absl::cord_internal::CordRepBtree; using CordRepBtreeReader = absl::cord_internal::CordRepBtreeReader; - // Constructs a `begin()` iterator from `tree`. `tree` must not be null. - explicit ChunkIterator(cord_internal::CordRep* tree); + // Constructs a `begin()` iterator from `tree`. + explicit ChunkIterator(absl::Nonnull<cord_internal::CordRep*> tree); // Constructs a `begin()` iterator from `cord`. - explicit ChunkIterator(const Cord* cord); + explicit ChunkIterator(absl::Nonnull<const Cord*> cord); // Initializes this instance from a tree. Invoked by constructors. - void InitTree(cord_internal::CordRep* tree); + void InitTree(absl::Nonnull<cord_internal::CordRep*> tree); // Removes `n` bytes from `current_chunk_`. Expects `n` to be smaller than // `current_chunk_.size()`. @@ -498,7 +500,7 @@ // The current leaf, or `nullptr` if the iterator points to short data. // If the current chunk is a substring node, current_leaf_ points to the // underlying flat or external node. - absl::cord_internal::CordRep* current_leaf_ = nullptr; + absl::Nullable<absl::cord_internal::CordRep*> current_leaf_ = nullptr; // The number of bytes left in the `Cord` over which we are iterating. size_t bytes_remaining_ = 0; @@ -555,13 +557,13 @@ using iterator = ChunkIterator; using const_iterator = ChunkIterator; - explicit ChunkRange(const Cord* cord) : cord_(cord) {} + explicit ChunkRange(absl::Nonnull<const Cord*> cord) : cord_(cord) {} ChunkIterator begin() const; ChunkIterator end() const; private: - const Cord* cord_; + absl::Nonnull<const Cord*> cord_; }; // Cord::Chunks() @@ -614,7 +616,7 @@ using iterator_category = std::input_iterator_tag; using value_type = char; using difference_type = ptrdiff_t; - using pointer = const char*; + using pointer = absl::Nonnull<const char*>; using reference = const char&; CharIterator() = default; @@ -629,7 +631,8 @@ friend Cord; private: - explicit CharIterator(const Cord* cord) : chunk_iterator_(cord) {} + explicit CharIterator(absl::Nonnull<const Cord*> cord) + : chunk_iterator_(cord) {} ChunkIterator chunk_iterator_; }; @@ -640,14 +643,14 @@ // advanced as a separate `Cord`. `n_bytes` must be less than or equal to the // number of bytes within the Cord; otherwise, behavior is undefined. It is // valid to pass `char_end()` and `0`. - static Cord AdvanceAndRead(CharIterator* it, size_t n_bytes); + static Cord AdvanceAndRead(absl::Nonnull<CharIterator*> it, size_t n_bytes); // Cord::Advance() // // Advances the `Cord::CharIterator` by `n_bytes`. `n_bytes` must be less than // or equal to the number of bytes remaining within the Cord; otherwise, // behavior is undefined. It is valid to pass `char_end()` and `0`. - static void Advance(CharIterator* it, size_t n_bytes); + static void Advance(absl::Nonnull<CharIterator*> it, size_t n_bytes); // Cord::ChunkRemaining() // @@ -696,13 +699,13 @@ using iterator = CharIterator; using const_iterator = CharIterator; - explicit CharRange(const Cord* cord) : cord_(cord) {} + explicit CharRange(absl::Nonnull<const Cord*> cord) : cord_(cord) {} CharIterator begin() const; CharIterator end() const; private: - const Cord* cord_; + absl::Nonnull<const Cord*> cord_; }; // Cord::Chars() @@ -761,7 +764,8 @@ CharIterator Find(const absl::Cord& needle) const; // Supports absl::Cord as a sink object for absl::Format(). - friend void AbslFormatFlush(absl::Cord* cord, absl::string_view part) { + friend void AbslFormatFlush(absl::Nonnull<absl::Cord*> cord, + absl::string_view part) { cord->Append(part); } @@ -831,7 +835,8 @@ friend bool operator==(const Cord& lhs, const Cord& rhs); friend bool operator==(const Cord& lhs, absl::string_view rhs); - friend const CordzInfo* GetCordzInfoForTesting(const Cord& cord); + friend absl::Nullable<const CordzInfo*> GetCordzInfoForTesting( + const Cord& cord); // Calls the provided function once for each cord chunk, in order. Unlike // Chunks(), this API will not allocate memory. @@ -858,19 +863,22 @@ InlineRep& operator=(const InlineRep& src); InlineRep& operator=(InlineRep&& src) noexcept; - explicit constexpr InlineRep(absl::string_view sv, CordRep* rep); + explicit constexpr InlineRep(absl::string_view sv, + absl::Nullable<CordRep*> rep); - void Swap(InlineRep* rhs); + void Swap(absl::Nonnull<InlineRep*> rhs); size_t size() const; - const char* data() const; // Returns nullptr if holding pointer - void set_data(const char* data, size_t n); // Discards pointer, if any - char* set_data(size_t n); // Write data to the result + // Returns nullptr if holding pointer + absl::Nullable<const char*> data() const; + // Discards pointer, if any + void set_data(absl::Nonnull<const char*> data, size_t n); + absl::Nonnull<char*> set_data(size_t n); // Write data to the result // Returns nullptr if holding bytes - absl::cord_internal::CordRep* tree() const; - absl::cord_internal::CordRep* as_tree() const; - const char* as_chars() const; + absl::Nullable<absl::cord_internal::CordRep*> tree() const; + absl::Nonnull<absl::cord_internal::CordRep*> as_tree() const; + absl::Nonnull<const char*> as_chars() const; // Returns non-null iff was holding a pointer - absl::cord_internal::CordRep* clear(); + absl::Nullable<absl::cord_internal::CordRep*> clear(); // Converts to pointer if necessary. void reduce_size(size_t n); // REQUIRES: holding data void remove_prefix(size_t n); // REQUIRES: holding data @@ -879,46 +887,52 @@ // Creates a CordRepFlat instance from the current inlined data with `extra' // bytes of desired additional capacity. - CordRepFlat* MakeFlatWithExtraCapacity(size_t extra); + absl::Nonnull<CordRepFlat*> MakeFlatWithExtraCapacity(size_t extra); // Sets the tree value for this instance. `rep` must not be null. // Requires the current instance to hold a tree, and a lock to be held on // any CordzInfo referenced by this instance. The latter is enforced through // the CordzUpdateScope argument. If the current instance is sampled, then // the CordzInfo instance is updated to reference the new `rep` value. - void SetTree(CordRep* rep, const CordzUpdateScope& scope); + void SetTree(absl::Nonnull<CordRep*> rep, const CordzUpdateScope& scope); // Identical to SetTree(), except that `rep` is allowed to be null, in // which case the current instance is reset to an empty value. - void SetTreeOrEmpty(CordRep* rep, const CordzUpdateScope& scope); + void SetTreeOrEmpty(absl::Nullable<CordRep*> rep, + const CordzUpdateScope& scope); // Sets the tree value for this instance, and randomly samples this cord. // This function disregards existing contents in `data_`, and should be // called when a Cord is 'promoted' from an 'uninitialized' or 'inlined' // value to a non-inlined (tree / ring) value. - void EmplaceTree(CordRep* rep, MethodIdentifier method); + void EmplaceTree(absl::Nonnull<CordRep*> rep, MethodIdentifier method); // Identical to EmplaceTree, except that it copies the parent stack from // the provided `parent` data if the parent is sampled. - void EmplaceTree(CordRep* rep, const InlineData& parent, + void EmplaceTree(absl::Nonnull<CordRep*> rep, const InlineData& parent, MethodIdentifier method); // Commits the change of a newly created, or updated `rep` root value into // this cord. `old_rep` indicates the old (inlined or tree) value of the // cord, and determines if the commit invokes SetTree() or EmplaceTree(). - void CommitTree(const CordRep* old_rep, CordRep* rep, - const CordzUpdateScope& scope, MethodIdentifier method); + void CommitTree(absl::Nullable<const CordRep*> old_rep, + absl::Nonnull<CordRep*> rep, const CordzUpdateScope& scope, + MethodIdentifier method); - void AppendTreeToInlined(CordRep* tree, MethodIdentifier method); - void AppendTreeToTree(CordRep* tree, MethodIdentifier method); - void AppendTree(CordRep* tree, MethodIdentifier method); - void PrependTreeToInlined(CordRep* tree, MethodIdentifier method); - void PrependTreeToTree(CordRep* tree, MethodIdentifier method); - void PrependTree(CordRep* tree, MethodIdentifier method); + void AppendTreeToInlined(absl::Nonnull<CordRep*> tree, + MethodIdentifier method); + void AppendTreeToTree(absl::Nonnull<CordRep*> tree, + MethodIdentifier method); + void AppendTree(absl::Nonnull<CordRep*> tree, MethodIdentifier method); + void PrependTreeToInlined(absl::Nonnull<CordRep*> tree, + MethodIdentifier method); + void PrependTreeToTree(absl::Nonnull<CordRep*> tree, + MethodIdentifier method); + void PrependTree(absl::Nonnull<CordRep*> tree, MethodIdentifier method); bool IsSame(const InlineRep& other) const { return data_ == other.data_; } - void CopyTo(std::string* dst) const { + void CopyTo(absl::Nonnull<std::string*> dst) const { // memcpy is much faster when operating on a known size. On most supported // platforms, the small string optimization is large enough that resizing // to 15 bytes does not cause a memory allocation. @@ -930,7 +944,7 @@ } // Copies the inline contents into `dst`. Assumes the cord is not empty. - void CopyToArray(char* dst) const; + void CopyToArray(absl::Nonnull<char*> dst) const; bool is_tree() const { return data_.is_tree(); } @@ -943,12 +957,12 @@ } // Returns the profiled CordzInfo, or nullptr if not sampled. - absl::cord_internal::CordzInfo* cordz_info() const { + absl::Nullable<absl::cord_internal::CordzInfo*> cordz_info() const { return data_.cordz_info(); } - // Sets the profiled CordzInfo. `cordz_info` must not be null. - void set_cordz_info(cord_internal::CordzInfo* cordz_info) { + // Sets the profiled CordzInfo. + void set_cordz_info(absl::Nonnull<cord_internal::CordzInfo*> cordz_info) { assert(cordz_info != nullptr); data_.set_cordz_info(cordz_info); } @@ -980,19 +994,19 @@ InlineRep contents_; // Helper for GetFlat() and TryFlat(). - static bool GetFlatAux(absl::cord_internal::CordRep* rep, - absl::string_view* fragment); + static bool GetFlatAux(absl::Nonnull<absl::cord_internal::CordRep*> rep, + absl::Nonnull<absl::string_view*> fragment); // Helper for ForEachChunk(). static void ForEachChunkAux( - absl::cord_internal::CordRep* rep, + absl::Nonnull<absl::cord_internal::CordRep*> rep, absl::FunctionRef<void(absl::string_view)> callback); // The destructor for non-empty Cords. void DestroyCordSlow(); // Out-of-line implementation of slower parts of logic. - void CopyToArraySlowPath(char* dst) const; + void CopyToArraySlowPath(absl::Nonnull<char*> dst) const; int CompareSlowPath(absl::string_view rhs, size_t compared_size, size_t size_to_compare) const; int CompareSlowPath(const Cord& rhs, size_t compared_size, @@ -1009,8 +1023,8 @@ // Returns a new reference to contents_.tree(), or steals an existing // reference if called on an rvalue. - absl::cord_internal::CordRep* TakeRep() const&; - absl::cord_internal::CordRep* TakeRep() &&; + absl::Nonnull<absl::cord_internal::CordRep*> TakeRep() const&; + absl::Nonnull<absl::cord_internal::CordRep*> TakeRep() &&; // Helper for Append(). template <typename C> @@ -1047,7 +1061,8 @@ friend class CrcCord; void SetCrcCordState(crc_internal::CrcCordState state); - const crc_internal::CrcCordState* MaybeGetCrcCordState() const; + absl::Nullable<const crc_internal::CrcCordState*> MaybeGetCrcCordState() + const; CharIterator FindImpl(CharIterator it, absl::string_view needle) const; }; @@ -1068,13 +1083,15 @@ // Does non-template-specific `CordRepExternal` initialization. // Requires `data` to be non-empty. -void InitializeCordRepExternal(absl::string_view data, CordRepExternal* rep); +void InitializeCordRepExternal(absl::string_view data, + absl::Nonnull<CordRepExternal*> rep); // Creates a new `CordRep` that owns `data` and `releaser` and returns a pointer // to it. Requires `data` to be non-empty. template <typename Releaser> // NOLINTNEXTLINE - suppress clang-tidy raw pointer return. -CordRep* NewExternalRep(absl::string_view data, Releaser&& releaser) { +absl::Nonnull<CordRep*> NewExternalRep(absl::string_view data, + Releaser&& releaser) { assert(!data.empty()); using ReleaserType = absl::decay_t<Releaser>; CordRepExternal* rep = new CordRepExternalImpl<ReleaserType>( @@ -1086,7 +1103,7 @@ // Overload for function reference types that dispatches using a function // pointer because there are no `alignof()` or `sizeof()` a function reference. // NOLINTNEXTLINE - suppress clang-tidy raw pointer return. -inline CordRep* NewExternalRep(absl::string_view data, +inline absl::Nonnull<CordRep*> NewExternalRep(absl::string_view data, void (&releaser)(absl::string_view)) { return NewExternalRep(data, &releaser); } @@ -1109,7 +1126,8 @@ return cord; } -constexpr Cord::InlineRep::InlineRep(absl::string_view sv, CordRep* rep) +constexpr Cord::InlineRep::InlineRep(absl::string_view sv, + absl::Nullable<CordRep*> rep) : data_(sv, rep) {} inline Cord::InlineRep::InlineRep(const Cord::InlineRep& src) @@ -1148,28 +1166,30 @@ return *this; } -inline void Cord::InlineRep::Swap(Cord::InlineRep* rhs) { +inline void Cord::InlineRep::Swap(absl::Nonnull<Cord::InlineRep*> rhs) { if (rhs == this) { return; } std::swap(data_, rhs->data_); } -inline const char* Cord::InlineRep::data() const { +inline absl::Nullable<const char*> Cord::InlineRep::data() const { return is_tree() ? nullptr : data_.as_chars(); } -inline const char* Cord::InlineRep::as_chars() const { +inline absl::Nonnull<const char*> Cord::InlineRep::as_chars() const { assert(!data_.is_tree()); return data_.as_chars(); } -inline absl::cord_internal::CordRep* Cord::InlineRep::as_tree() const { +inline absl::Nonnull<absl::cord_internal::CordRep*> Cord::InlineRep::as_tree() + const { assert(data_.is_tree()); return data_.as_tree(); } -inline absl::cord_internal::CordRep* Cord::InlineRep::tree() const { +inline absl::Nullable<absl::cord_internal::CordRep*> Cord::InlineRep::tree() + const { if (is_tree()) { return as_tree(); } else { @@ -1181,8 +1201,8 @@ return is_tree() ? as_tree()->length : inline_size(); } -inline cord_internal::CordRepFlat* Cord::InlineRep::MakeFlatWithExtraCapacity( - size_t extra) { +inline absl::Nonnull<cord_internal::CordRepFlat*> +Cord::InlineRep::MakeFlatWithExtraCapacity(size_t extra) { static_assert(cord_internal::kMinFlatLength >= sizeof(data_), ""); size_t len = data_.inline_size(); auto* result = CordRepFlat::New(len + extra); @@ -1191,20 +1211,21 @@ return result; } -inline void Cord::InlineRep::EmplaceTree(CordRep* rep, +inline void Cord::InlineRep::EmplaceTree(absl::Nonnull<CordRep*> rep, MethodIdentifier method) { assert(rep); data_.make_tree(rep); CordzInfo::MaybeTrackCord(data_, method); } -inline void Cord::InlineRep::EmplaceTree(CordRep* rep, const InlineData& parent, +inline void Cord::InlineRep::EmplaceTree(absl::Nonnull<CordRep*> rep, + const InlineData& parent, MethodIdentifier method) { data_.make_tree(rep); CordzInfo::MaybeTrackCord(data_, parent, method); } -inline void Cord::InlineRep::SetTree(CordRep* rep, +inline void Cord::InlineRep::SetTree(absl::Nonnull<CordRep*> rep, const CordzUpdateScope& scope) { assert(rep); assert(data_.is_tree()); @@ -1212,7 +1233,7 @@ scope.SetCordRep(rep); } -inline void Cord::InlineRep::SetTreeOrEmpty(CordRep* rep, +inline void Cord::InlineRep::SetTreeOrEmpty(absl::Nullable<CordRep*> rep, const CordzUpdateScope& scope) { assert(data_.is_tree()); if (rep) { @@ -1223,7 +1244,8 @@ scope.SetCordRep(rep); } -inline void Cord::InlineRep::CommitTree(const CordRep* old_rep, CordRep* rep, +inline void Cord::InlineRep::CommitTree(absl::Nullable<const CordRep*> old_rep, + absl::Nonnull<CordRep*> rep, const CordzUpdateScope& scope, MethodIdentifier method) { if (old_rep) { @@ -1233,7 +1255,7 @@ } } -inline absl::cord_internal::CordRep* Cord::InlineRep::clear() { +inline absl::Nullable<absl::cord_internal::CordRep*> Cord::InlineRep::clear() { if (is_tree()) { CordzInfo::MaybeUntrackCord(cordz_info()); } @@ -1242,7 +1264,7 @@ return result; } -inline void Cord::InlineRep::CopyToArray(char* dst) const { +inline void Cord::InlineRep::CopyToArray(absl::Nonnull<char*> dst) const { assert(!is_tree()); size_t n = inline_size(); assert(n != 0); @@ -1423,7 +1445,8 @@ return EqualsImpl(rhs, rhs_size); } -inline void Cord::ChunkIterator::InitTree(cord_internal::CordRep* tree) { +inline void Cord::ChunkIterator::InitTree( + absl::Nonnull<cord_internal::CordRep*> tree) { tree = cord_internal::SkipCrcNode(tree); if (tree->tag == cord_internal::BTREE) { current_chunk_ = btree_reader_.Init(tree->btree()); @@ -1433,12 +1456,13 @@ } } -inline Cord::ChunkIterator::ChunkIterator(cord_internal::CordRep* tree) { +inline Cord::ChunkIterator::ChunkIterator( + absl::Nonnull<cord_internal::CordRep*> tree) { bytes_remaining_ = tree->length; InitTree(tree); } -inline Cord::ChunkIterator::ChunkIterator(const Cord* cord) { +inline Cord::ChunkIterator::ChunkIterator(absl::Nonnull<const Cord*> cord) { if (CordRep* tree = cord->contents_.tree()) { bytes_remaining_ = tree->length; if (ABSL_PREDICT_TRUE(bytes_remaining_ != 0)) { @@ -1578,12 +1602,13 @@ return chunk_iterator_->data(); } -inline Cord Cord::AdvanceAndRead(CharIterator* it, size_t n_bytes) { +inline Cord Cord::AdvanceAndRead(absl::Nonnull<CharIterator*> it, + size_t n_bytes) { assert(it != nullptr); return it->chunk_iterator_.AdvanceAndReadBytes(n_bytes); } -inline void Cord::Advance(CharIterator* it, size_t n_bytes) { +inline void Cord::Advance(absl::Nonnull<CharIterator*> it, size_t n_bytes) { assert(it != nullptr); it->chunk_iterator_.AdvanceBytes(n_bytes); }
diff --git a/absl/strings/cord_analysis.cc b/absl/strings/cord_analysis.cc index 5418a32..19b0fa4 100644 --- a/absl/strings/cord_analysis.cc +++ b/absl/strings/cord_analysis.cc
@@ -20,6 +20,7 @@ #include <unordered_set> #include "absl/base/config.h" +#include "absl/base/nullability.h" #include "absl/strings/internal/cord_data_edge.h" #include "absl/strings/internal/cord_internal.h" #include "absl/strings/internal/cord_rep_btree.h" @@ -38,13 +39,15 @@ template <Mode mode> struct CordRepRef { // Instantiates a CordRepRef instance. - explicit CordRepRef(const CordRep* r) : rep(r) {} + explicit CordRepRef(absl::Nonnull<const CordRep*> r) : rep(r) {} // Creates a child reference holding the provided child. // Overloaded to add cumulative reference count for kFairShare. - CordRepRef Child(const CordRep* child) const { return CordRepRef(child); } + CordRepRef Child(absl::Nonnull<const CordRep*> child) const { + return CordRepRef(child); + } - const CordRep* rep; + absl::Nonnull<const CordRep*> rep; }; // RawUsage holds the computed total number of bytes. @@ -63,7 +66,7 @@ struct RawUsage<Mode::kTotalMorePrecise> { size_t total = 0; // TODO(b/289250880): Replace this with a flat_hash_set. - std::unordered_set<const CordRep*> counted; + std::unordered_set<absl::Nonnull<const CordRep*>> counted; void Add(size_t size, CordRepRef<Mode::kTotalMorePrecise> repref) { if (counted.insert(repref.rep).second) { @@ -87,15 +90,15 @@ template <> struct CordRepRef<Mode::kFairShare> { // Creates a CordRepRef with the provided rep and top (parent) fraction. - explicit CordRepRef(const CordRep* r, double frac = 1.0) + explicit CordRepRef(absl::Nonnull<const CordRep*> r, double frac = 1.0) : rep(r), fraction(MaybeDiv(frac, r->refcount.Get())) {} // Returns a CordRepRef with a fraction of `this->fraction / child.refcount` - CordRepRef Child(const CordRep* child) const { + CordRepRef Child(absl::Nonnull<const CordRep*> child) const { return CordRepRef(child, fraction); } - const CordRep* rep; + absl::Nonnull<const CordRep*> rep; double fraction; }; @@ -147,7 +150,7 @@ } template <Mode mode> -size_t GetEstimatedUsage(const CordRep* rep) { +size_t GetEstimatedUsage(absl::Nonnull<const CordRep*> rep) { // Zero initialized memory usage totals. RawUsage<mode> raw_usage; @@ -176,15 +179,15 @@ } // namespace -size_t GetEstimatedMemoryUsage(const CordRep* rep) { +size_t GetEstimatedMemoryUsage(absl::Nonnull<const CordRep*> rep) { return GetEstimatedUsage<Mode::kTotal>(rep); } -size_t GetEstimatedFairShareMemoryUsage(const CordRep* rep) { +size_t GetEstimatedFairShareMemoryUsage(absl::Nonnull<const CordRep*> rep) { return GetEstimatedUsage<Mode::kFairShare>(rep); } -size_t GetMorePreciseMemoryUsage(const CordRep* rep) { +size_t GetMorePreciseMemoryUsage(absl::Nonnull<const CordRep*> rep) { return GetEstimatedUsage<Mode::kTotalMorePrecise>(rep); }
diff --git a/absl/strings/cord_analysis.h b/absl/strings/cord_analysis.h index 9b9527a..f8ce348 100644 --- a/absl/strings/cord_analysis.h +++ b/absl/strings/cord_analysis.h
@@ -19,6 +19,7 @@ #include <cstdint> #include "absl/base/config.h" +#include "absl/base/nullability.h" #include "absl/strings/internal/cord_internal.h" namespace absl { @@ -28,7 +29,7 @@ // Returns the *approximate* number of bytes held in full or in part by this // Cord (which may not remain the same between invocations). Cords that share // memory could each be "charged" independently for the same shared memory. -size_t GetEstimatedMemoryUsage(const CordRep* rep); +size_t GetEstimatedMemoryUsage(absl::Nonnull<const CordRep*> rep); // Returns the *approximate* number of bytes held in full or in part by this // Cord for the distinct memory held by this cord. This is similar to @@ -46,13 +47,13 @@ // // This is more expensive than `GetEstimatedMemoryUsage()` as it requires // deduplicating all memory references. -size_t GetMorePreciseMemoryUsage(const CordRep* rep); +size_t GetMorePreciseMemoryUsage(absl::Nonnull<const CordRep*> rep); // Returns the *approximate* number of bytes held in full or in part by this // CordRep weighted by the sharing ratio of that data. For example, if some data // edge is shared by 4 different Cords, then each cord is attribute 1/4th of // the total memory usage as a 'fair share' of the total memory usage. -size_t GetEstimatedFairShareMemoryUsage(const CordRep* rep); +size_t GetEstimatedFairShareMemoryUsage(absl::Nonnull<const CordRep*> rep); } // namespace cord_internal ABSL_NAMESPACE_END
diff --git a/absl/strings/cordz_test_helpers.h b/absl/strings/cordz_test_helpers.h index e410eec..619f13c 100644 --- a/absl/strings/cordz_test_helpers.h +++ b/absl/strings/cordz_test_helpers.h
@@ -21,6 +21,7 @@ #include "gtest/gtest.h" #include "absl/base/config.h" #include "absl/base/macros.h" +#include "absl/base/nullability.h" #include "absl/strings/cord.h" #include "absl/strings/internal/cord_internal.h" #include "absl/strings/internal/cordz_info.h" @@ -33,15 +34,16 @@ ABSL_NAMESPACE_BEGIN // Returns the CordzInfo for the cord, or nullptr if the cord is not sampled. -inline const cord_internal::CordzInfo* GetCordzInfoForTesting( +inline absl::Nullable<const cord_internal::CordzInfo*> GetCordzInfoForTesting( const Cord& cord) { if (!cord.contents_.is_tree()) return nullptr; return cord.contents_.cordz_info(); } // Returns true if the provided cordz_info is in the list of sampled cords. -inline bool CordzInfoIsListed(const cord_internal::CordzInfo* cordz_info, - cord_internal::CordzSampleToken token = {}) { +inline bool CordzInfoIsListed( + absl::Nonnull<const cord_internal::CordzInfo*> cordz_info, + cord_internal::CordzSampleToken token = {}) { for (const cord_internal::CordzInfo& info : token) { if (cordz_info == &info) return true; } @@ -119,7 +121,7 @@ // Wrapper struct managing a small CordRep `rep` struct TestCordRep { - cord_internal::CordRepFlat* rep; + absl::Nonnull<cord_internal::CordRepFlat*> rep; TestCordRep() { rep = cord_internal::CordRepFlat::New(100);
diff --git a/absl/strings/internal/str_format/arg.cc b/absl/strings/internal/str_format/arg.cc index c0a9a28..eeb2108 100644 --- a/absl/strings/internal/str_format/arg.cc +++ b/absl/strings/internal/str_format/arg.cc
@@ -18,15 +18,28 @@ // #include "absl/strings/internal/str_format/arg.h" +#include <algorithm> #include <cassert> -#include <cerrno> +#include <cstddef> +#include <cstdint> #include <cstdlib> +#include <cstring> +#include <cwchar> #include <string> #include <type_traits> -#include "absl/base/port.h" +#include "absl/base/config.h" +#include "absl/base/optimization.h" +#include "absl/container/fixed_array.h" +#include "absl/numeric/int128.h" +#include "absl/strings/internal/str_format/extension.h" #include "absl/strings/internal/str_format/float_conversion.h" #include "absl/strings/numbers.h" +#include "absl/strings/string_view.h" + +#if defined(ABSL_HAVE_STD_STRING_VIEW) +#include <string_view> +#endif namespace absl { ABSL_NAMESPACE_BEGIN @@ -298,6 +311,83 @@ conv.has_left_flag()); } +struct ShiftState { + bool saw_high_surrogate = false; + uint8_t bits = 0; +}; + +// Converts `v` from UTF-16 or UTF-32 to UTF-8 and writes to `buf`. `buf` is +// assumed to have enough space for the output. `s` is used to carry state +// between successive calls with a UTF-16 surrogate pair. Returns the number of +// chars written, or `static_cast<size_t>(-1)` on failure. +// +// This is basically std::wcrtomb(), but always outputting UTF-8 instead of +// respecting the current locale. +inline size_t WideToUtf8(wchar_t wc, char *buf, ShiftState &s) { + const auto v = static_cast<uint32_t>(wc); + if (v < 0x80) { + *buf = static_cast<char>(v); + return 1; + } else if (v < 0x800) { + *buf++ = static_cast<char>(0xc0 | (v >> 6)); + *buf = static_cast<char>(0x80 | (v & 0x3f)); + return 2; + } else if (v < 0xd800 || (v - 0xe000) < 0x2000) { + *buf++ = static_cast<char>(0xe0 | (v >> 12)); + *buf++ = static_cast<char>(0x80 | ((v >> 6) & 0x3f)); + *buf = static_cast<char>(0x80 | (v & 0x3f)); + return 3; + } else if ((v - 0x10000) < 0x100000) { + *buf++ = static_cast<char>(0xf0 | (v >> 18)); + *buf++ = static_cast<char>(0x80 | ((v >> 12) & 0x3f)); + *buf++ = static_cast<char>(0x80 | ((v >> 6) & 0x3f)); + *buf = static_cast<char>(0x80 | (v & 0x3f)); + return 4; + } else if (v < 0xdc00) { + s.saw_high_surrogate = true; + s.bits = static_cast<uint8_t>(v & 0x3); + const uint8_t high_bits = ((v >> 6) & 0xf) + 1; + *buf++ = static_cast<char>(0xf0 | (high_bits >> 2)); + *buf = + static_cast<char>(0x80 | static_cast<uint8_t>((high_bits & 0x3) << 4) | + static_cast<uint8_t>((v >> 2) & 0xf)); + return 2; + } else if (v < 0xe000 && s.saw_high_surrogate) { + *buf++ = static_cast<char>(0x80 | static_cast<uint8_t>(s.bits << 4) | + static_cast<uint8_t>((v >> 6) & 0xf)); + *buf = static_cast<char>(0x80 | (v & 0x3f)); + s.saw_high_surrogate = false; + s.bits = 0; + return 2; + } else { + return static_cast<size_t>(-1); + } +} + +inline bool ConvertStringArg(const wchar_t *v, + size_t len, + const FormatConversionSpecImpl conv, + FormatSinkImpl *sink) { + FixedArray<char> mb(len * 4); + ShiftState s; + size_t chars_written = 0; + for (size_t i = 0; i < len; ++i) { + const size_t chars = WideToUtf8(v[i], &mb[chars_written], s); + if (chars == static_cast<size_t>(-1)) { return false; } + chars_written += chars; + } + return ConvertStringArg(string_view(mb.data(), chars_written), conv, sink); +} + +bool ConvertWCharTImpl(wchar_t v, const FormatConversionSpecImpl conv, + FormatSinkImpl *sink) { + char mb[4]; + ShiftState s; + const size_t chars_written = WideToUtf8(v, mb, s); + return chars_written != static_cast<size_t>(-1) && !s.saw_high_surrogate && + ConvertStringArg(string_view(mb, chars_written), conv, sink); +} + } // namespace bool ConvertBoolArg(bool v, FormatSinkImpl *sink) { @@ -316,11 +406,14 @@ // This odd casting is due to a bug in -Wswitch behavior in gcc49 which causes // it to complain about a switch/case type mismatch, even though both are - // FormatConverionChar. Likely this is because at this point + // FormatConversionChar. Likely this is because at this point // FormatConversionChar is declared, but not defined. switch (static_cast<uint8_t>(conv.conversion_char())) { case static_cast<uint8_t>(FormatConversionCharInternal::c): - return ConvertCharImpl(static_cast<char>(v), conv, sink); + return (std::is_same<T, wchar_t>::value || + (conv.length_mod() == LengthMod::l)) + ? ConvertWCharTImpl(static_cast<wchar_t>(v), conv, sink) + : ConvertCharImpl(static_cast<char>(v), conv, sink); case static_cast<uint8_t>(FormatConversionCharInternal::o): as_digits.PrintAsOct(static_cast<U>(v)); @@ -372,6 +465,8 @@ template bool ConvertIntArg<unsigned char>(unsigned char v, FormatConversionSpecImpl conv, FormatSinkImpl *sink); +template bool ConvertIntArg<wchar_t>(wchar_t v, FormatConversionSpecImpl conv, + FormatSinkImpl *sink); template bool ConvertIntArg<short>(short v, // NOLINT FormatConversionSpecImpl conv, FormatSinkImpl *sink); @@ -403,16 +498,29 @@ return {ConvertStringArg(v, conv, sink)}; } +StringConvertResult FormatConvertImpl(const std::wstring &v, + const FormatConversionSpecImpl conv, + FormatSinkImpl *sink) { + return {ConvertStringArg(v.data(), v.size(), conv, sink)}; +} + StringConvertResult FormatConvertImpl(string_view v, const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { return {ConvertStringArg(v, conv, sink)}; } -ArgConvertResult<FormatConversionCharSetUnion( - FormatConversionCharSetInternal::s, FormatConversionCharSetInternal::p)> -FormatConvertImpl(const char *v, const FormatConversionSpecImpl conv, - FormatSinkImpl *sink) { +#if defined(ABSL_HAVE_STD_STRING_VIEW) +StringConvertResult FormatConvertImpl(std::wstring_view v, + const FormatConversionSpecImpl conv, + FormatSinkImpl* sink) { + return {ConvertStringArg(v.data(), v.size(), conv, sink)}; +} +#endif + +StringPtrConvertResult 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; @@ -427,6 +535,30 @@ return {ConvertStringArg(string_view(v, len), conv, sink)}; } +StringPtrConvertResult FormatConvertImpl(const wchar_t* 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) { + len = 0; + } else if (conv.precision() < 0) { + len = std::wcslen(v); + } else { + // If precision is set, we look for the NUL-terminator on the valid range. + len = static_cast<size_t>(std::find(v, v + conv.precision(), L'\0') - v); + } + return {ConvertStringArg(v, len, conv, sink)}; +} + +StringPtrConvertResult FormatConvertImpl(std::nullptr_t, + const FormatConversionSpecImpl conv, + FormatSinkImpl* sink) { + return FormatConvertImpl(static_cast<const char*>(nullptr), conv, sink); +} + // ==================== Raw pointers ==================== ArgConvertResult<FormatConversionCharSetInternal::p> FormatConvertImpl( VoidPtr v, const FormatConversionSpecImpl conv, FormatSinkImpl *sink) { @@ -461,6 +593,11 @@ FormatSinkImpl *sink) { return {ConvertIntArg(v, conv, sink)}; } +CharConvertResult FormatConvertImpl(wchar_t v, + const FormatConversionSpecImpl conv, + FormatSinkImpl* sink) { + return {ConvertIntArg(v, conv, sink)}; +} // ==================== Ints ==================== IntegralConvertResult FormatConvertImpl(signed char v,
diff --git a/absl/strings/internal/str_format/arg.h b/absl/strings/internal/str_format/arg.h index 20483af..309161d 100644 --- a/absl/strings/internal/str_format/arg.h +++ b/absl/strings/internal/str_format/arg.h
@@ -19,8 +19,9 @@ #include <wchar.h> #include <algorithm> +#include <cstddef> +#include <cstdint> #include <cstdio> -#include <iomanip> #include <limits> #include <memory> #include <sstream> @@ -28,13 +29,18 @@ #include <type_traits> #include <utility> -#include "absl/base/port.h" +#include "absl/base/config.h" +#include "absl/base/optimization.h" #include "absl/meta/type_traits.h" #include "absl/numeric/int128.h" #include "absl/strings/has_absl_stringify.h" #include "absl/strings/internal/str_format/extension.h" #include "absl/strings/string_view.h" +#if defined(ABSL_HAVE_STD_STRING_VIEW) +#include <string_view> +#endif + namespace absl { ABSL_NAMESPACE_BEGIN @@ -97,6 +103,9 @@ extern template bool ConvertIntArg<unsigned char>(unsigned char v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); +extern template bool ConvertIntArg<wchar_t>(wchar_t v, + FormatConversionSpecImpl conv, + FormatSinkImpl* sink); extern template bool ConvertIntArg<short>(short v, // NOLINT FormatConversionSpecImpl conv, FormatSinkImpl* sink); @@ -203,30 +212,49 @@ return C; } -using StringConvertResult = ArgConvertResult<FormatConversionCharSetUnion( - FormatConversionCharSetInternal::s, FormatConversionCharSetInternal::v)>; ArgConvertResult<FormatConversionCharSetInternal::p> FormatConvertImpl( VoidPtr v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); // Strings. +using StringConvertResult = ArgConvertResult<FormatConversionCharSetUnion( + FormatConversionCharSetInternal::s, + FormatConversionCharSetInternal::v)>; StringConvertResult FormatConvertImpl(const std::string& v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); +StringConvertResult FormatConvertImpl(const std::wstring& v, + FormatConversionSpecImpl conv, + FormatSinkImpl* sink); StringConvertResult FormatConvertImpl(string_view v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); -#if defined(ABSL_HAVE_STD_STRING_VIEW) && !defined(ABSL_USES_STD_STRING_VIEW) +#if defined(ABSL_HAVE_STD_STRING_VIEW) +StringConvertResult FormatConvertImpl(std::wstring_view v, + FormatConversionSpecImpl conv, + FormatSinkImpl* sink); +#if !defined(ABSL_USES_STD_STRING_VIEW) inline StringConvertResult FormatConvertImpl(std::string_view v, FormatConversionSpecImpl conv, FormatSinkImpl* sink) { return FormatConvertImpl(absl::string_view(v.data(), v.size()), conv, sink); } -#endif // ABSL_HAVE_STD_STRING_VIEW && !ABSL_USES_STD_STRING_VIEW +#endif // !ABSL_USES_STD_STRING_VIEW +#endif // ABSL_HAVE_STD_STRING_VIEW -ArgConvertResult<FormatConversionCharSetUnion( - FormatConversionCharSetInternal::s, FormatConversionCharSetInternal::p)> -FormatConvertImpl(const char* v, const FormatConversionSpecImpl conv, - FormatSinkImpl* sink); +using StringPtrConvertResult = ArgConvertResult<FormatConversionCharSetUnion( + FormatConversionCharSetInternal::s, + FormatConversionCharSetInternal::p)>; +StringPtrConvertResult FormatConvertImpl(const char* v, + FormatConversionSpecImpl conv, + FormatSinkImpl* sink); +StringPtrConvertResult FormatConvertImpl(const wchar_t* v, + FormatConversionSpecImpl conv, + FormatSinkImpl* sink); +// This overload is needed to disambiguate, since `nullptr` could match either +// of the other overloads equally well. +StringPtrConvertResult FormatConvertImpl(std::nullptr_t, + FormatConversionSpecImpl conv, + FormatSinkImpl* sink); template <class AbslCord, typename std::enable_if<std::is_same< AbslCord, absl::Cord>::value>::type* = nullptr> @@ -280,6 +308,9 @@ // Chars. CharConvertResult FormatConvertImpl(char v, FormatConversionSpecImpl conv, FormatSinkImpl* sink); +CharConvertResult FormatConvertImpl(wchar_t v, + FormatConversionSpecImpl conv, + FormatSinkImpl* sink); // Ints. IntegralConvertResult FormatConvertImpl(signed char v, @@ -441,6 +472,7 @@ // Anything with a user-defined Convert will get its own vtable. // For everything else: // - Decay char* and char arrays into `const char*` + // - Decay wchar_t* and wchar_t arrays into `const wchar_t*` // - Decay any other pointer to `const void*` // - Decay all enums to the integral promotion of their underlying type. // - Decay function pointers to void*. @@ -452,9 +484,13 @@ using type = typename std::conditional< !kHasUserDefined && std::is_convertible<T, const char*>::value, const char*, - typename std::conditional<!kHasUserDefined && - std::is_convertible<T, VoidPtr>::value, - VoidPtr, const T&>::type>::type; + typename std::conditional< + !kHasUserDefined && std::is_convertible<T, const wchar_t*>::value, + const wchar_t*, + typename std::conditional< + !kHasUserDefined && std::is_convertible<T, VoidPtr>::value, + VoidPtr, + const T&>::type>::type>::type; }; template <typename T> struct DecayType< @@ -585,7 +621,7 @@ E template bool FormatArgImpl::Dispatch<T>(Data, FormatConversionSpecImpl, \ void*) -#define ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_(...) \ +#define ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_NO_WSTRING_VIEW_(...) \ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(str_format_internal::VoidPtr, \ __VA_ARGS__); \ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(bool, __VA_ARGS__); \ @@ -611,7 +647,19 @@ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(long double, __VA_ARGS__); \ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(const char*, __VA_ARGS__); \ ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(std::string, __VA_ARGS__); \ - ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(string_view, __VA_ARGS__) + ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(string_view, __VA_ARGS__); \ + ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(const wchar_t*, __VA_ARGS__); \ + ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(std::wstring, __VA_ARGS__) + +#if defined(ABSL_HAVE_STD_STRING_VIEW) +#define ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_(...) \ + ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_NO_WSTRING_VIEW_( \ + __VA_ARGS__); \ + ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(std::wstring_view, __VA_ARGS__) +#else +#define ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_(...) \ + ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_NO_WSTRING_VIEW_(__VA_ARGS__) +#endif ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_(extern);
diff --git a/absl/strings/internal/str_format/arg_test.cc b/absl/strings/internal/str_format/arg_test.cc index 1261937..f663d7c 100644 --- a/absl/strings/internal/str_format/arg_test.cc +++ b/absl/strings/internal/str_format/arg_test.cc
@@ -14,9 +14,10 @@ #include "absl/strings/internal/str_format/arg.h" -#include <ostream> +#include <limits> #include <string> #include "gtest/gtest.h" +#include "absl/base/config.h" #include "absl/strings/str_format.h" namespace absl { @@ -93,6 +94,21 @@ FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(kMyArray))); } +extern const wchar_t kMyWCharTArray[]; + +TEST_F(FormatArgImplTest, WCharTArraysDecayToWCharTPtr) { + const wchar_t* a = L""; + EXPECT_EQ(FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(a)), + FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(L""))); + EXPECT_EQ(FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(a)), + FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(L"A"))); + EXPECT_EQ(FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(a)), + FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(L"ABC"))); + EXPECT_EQ( + FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(a)), + FormatArgImplFriend::GetVTablePtrForTest(FormatArgImpl(kMyWCharTArray))); +} + TEST_F(FormatArgImplTest, OtherPtrDecayToVoidPtr) { auto expected = FormatArgImplFriend::GetVTablePtrForTest( FormatArgImpl(static_cast<void *>(nullptr))); @@ -124,6 +140,22 @@ } const char kMyArray[] = "ABCDE"; +TEST_F(FormatArgImplTest, WorksWithWCharTArraysOfUnknownSize) { + std::string s; + FormatSinkImpl sink(&s); + FormatConversionSpecImpl conv; + FormatConversionSpecImplFriend::SetConversionChar( + FormatConversionCharInternal::s, &conv); + FormatConversionSpecImplFriend::SetFlags(Flags(), &conv); + FormatConversionSpecImplFriend::SetWidth(-1, &conv); + FormatConversionSpecImplFriend::SetPrecision(-1, &conv); + EXPECT_TRUE( + FormatArgImplFriend::Convert(FormatArgImpl(kMyWCharTArray), conv, &sink)); + sink.Flush(); + EXPECT_EQ("ABCDE", s); +} +const wchar_t kMyWCharTArray[] = L"ABCDE"; + } // namespace } // namespace str_format_internal ABSL_NAMESPACE_END
diff --git a/absl/strings/internal/str_format/bind.cc b/absl/strings/internal/str_format/bind.cc index 77a4222..87e23b5 100644 --- a/absl/strings/internal/str_format/bind.cc +++ b/absl/strings/internal/str_format/bind.cc
@@ -14,10 +14,24 @@ #include "absl/strings/internal/str_format/bind.h" +#include <algorithm> +#include <cassert> #include <cerrno> +#include <cstddef> +#include <cstdio> +#include <ios> #include <limits> +#include <ostream> #include <sstream> #include <string> +#include "absl/base/config.h" +#include "absl/base/optimization.h" +#include "absl/strings/internal/str_format/arg.h" +#include "absl/strings/internal/str_format/constexpr_parser.h" +#include "absl/strings/internal/str_format/extension.h" +#include "absl/strings/internal/str_format/output.h" +#include "absl/strings/string_view.h" +#include "absl/types/span.h" namespace absl { ABSL_NAMESPACE_BEGIN @@ -90,6 +104,8 @@ } else { FormatConversionSpecImplFriend::SetFlags(unbound->flags, bound); } + + FormatConversionSpecImplFriend::SetLengthMod(unbound->length_mod, bound); } else { FormatConversionSpecImplFriend::SetFlags(unbound->flags, bound); FormatConversionSpecImplFriend::SetWidth(-1, bound); @@ -215,7 +231,7 @@ return *out; } -std::string FormatPack(const UntypedFormatSpecImpl format, +std::string FormatPack(UntypedFormatSpecImpl format, absl::Span<const FormatArgImpl> args) { std::string out; if (ABSL_PREDICT_FALSE(!FormatUntyped(&out, format, args))) {
diff --git a/absl/strings/internal/str_format/bind.h b/absl/strings/internal/str_format/bind.h index 5e2a43d..120bc35 100644 --- a/absl/strings/internal/str_format/bind.h +++ b/absl/strings/internal/str_format/bind.h
@@ -15,16 +15,19 @@ #ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_BIND_H_ #define ABSL_STRINGS_INTERNAL_STR_FORMAT_BIND_H_ -#include <array> +#include <cassert> #include <cstdio> -#include <sstream> +#include <ostream> #include <string> -#include "absl/base/port.h" +#include "absl/base/config.h" #include "absl/container/inlined_vector.h" #include "absl/strings/internal/str_format/arg.h" #include "absl/strings/internal/str_format/checker.h" +#include "absl/strings/internal/str_format/constexpr_parser.h" +#include "absl/strings/internal/str_format/extension.h" #include "absl/strings/internal/str_format/parser.h" +#include "absl/strings/string_view.h" #include "absl/types/span.h" #include "absl/utility/utility.h" @@ -203,7 +206,7 @@ std::string& AppendPack(std::string* out, UntypedFormatSpecImpl format, absl::Span<const FormatArgImpl> args); -std::string FormatPack(const UntypedFormatSpecImpl format, +std::string FormatPack(UntypedFormatSpecImpl format, absl::Span<const FormatArgImpl> args); int FprintF(std::FILE* output, UntypedFormatSpecImpl format,
diff --git a/absl/strings/internal/str_format/constexpr_parser.h b/absl/strings/internal/str_format/constexpr_parser.h index b70a16e..8f59387 100644 --- a/absl/strings/internal/str_format/constexpr_parser.h +++ b/absl/strings/internal/str_format/constexpr_parser.h
@@ -17,17 +17,18 @@ #include <cassert> #include <cstdint> +#include <cstdio> #include <limits> +#include "absl/base/config.h" #include "absl/base/const_init.h" +#include "absl/base/optimization.h" #include "absl/strings/internal/str_format/extension.h" namespace absl { ABSL_NAMESPACE_BEGIN namespace str_format_internal { -enum class LengthMod : std::uint8_t { h, hh, l, ll, L, j, z, t, q, none }; - // The analyzed properties of a single specified conversion. struct UnboundConversion { // This is a user defined default constructor on purpose to skip the @@ -306,7 +307,6 @@ if (ABSL_PREDICT_FALSE(!tag.is_length())) return nullptr; // It is a length modifier. - using str_format_internal::LengthMod; LengthMod length_mod = tag.as_length(); ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR(); if (c == 'h' && length_mod == LengthMod::h) { @@ -322,6 +322,11 @@ if (ABSL_PREDICT_FALSE(c == 'v')) return nullptr; if (ABSL_PREDICT_FALSE(!tag.is_conv())) return nullptr; + + // `wchar_t` args are marked non-basic so `Bind()` will copy the length mod. + if (conv->length_mod == LengthMod::l && c == 'c') { + conv->flags = conv->flags | Flags::kNonBasic; + } } #undef ABSL_FORMAT_PARSER_INTERNAL_GET_CHAR
diff --git a/absl/strings/internal/str_format/convert_test.cc b/absl/strings/internal/str_format/convert_test.cc index d14ecb2..7f22277 100644 --- a/absl/strings/internal/str_format/convert_test.cc +++ b/absl/strings/internal/str_format/convert_test.cc
@@ -12,25 +12,43 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include <errno.h> +#include <assert.h> +#include <locale.h> #include <stdarg.h> #include <stdio.h> #include <algorithm> -#include <cctype> +#include <climits> #include <cmath> +#include <cstdlib> +#include <cstring> +#include <cwctype> #include <limits> +#include <set> +#include <sstream> #include <string> #include <thread> // NOLINT +#include <type_traits> +#include <vector> #include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/base/attributes.h" #include "absl/base/internal/raw_logging.h" #include "absl/log/log.h" +#include "absl/numeric/int128.h" +#include "absl/strings/ascii.h" +#include "absl/strings/internal/str_format/arg.h" #include "absl/strings/internal/str_format/bind.h" #include "absl/strings/match.h" +#include "absl/strings/str_format.h" +#include "absl/strings/string_view.h" #include "absl/types/optional.h" +#include "absl/types/span.h" + +#if defined(ABSL_HAVE_STD_STRING_VIEW) +#include <string_view> +#endif namespace absl { ABSL_NAMESPACE_BEGIN @@ -49,36 +67,103 @@ return N; } -std::string LengthModFor(float) { return ""; } -std::string LengthModFor(double) { return ""; } -std::string LengthModFor(long double) { return "L"; } -std::string LengthModFor(char) { return "hh"; } -std::string LengthModFor(signed char) { return "hh"; } -std::string LengthModFor(unsigned char) { return "hh"; } -std::string LengthModFor(short) { return "h"; } // NOLINT -std::string LengthModFor(unsigned short) { return "h"; } // NOLINT -std::string LengthModFor(int) { return ""; } -std::string LengthModFor(unsigned) { return ""; } -std::string LengthModFor(long) { return "l"; } // NOLINT -std::string LengthModFor(unsigned long) { return "l"; } // NOLINT -std::string LengthModFor(long long) { return "ll"; } // NOLINT -std::string LengthModFor(unsigned long long) { return "ll"; } // NOLINT +template <typename T> +struct AlwaysFalse : std::false_type {}; + +template <typename T> +std::string LengthModFor() { + static_assert(AlwaysFalse<T>::value, "Unsupported type"); + return ""; +} +template <> +std::string LengthModFor<char>() { + return "hh"; +} +template <> +std::string LengthModFor<signed char>() { + return "hh"; +} +template <> +std::string LengthModFor<unsigned char>() { + return "hh"; +} +template <> +std::string LengthModFor<short>() { // NOLINT + return "h"; +} +template <> +std::string LengthModFor<unsigned short>() { // NOLINT + return "h"; +} +template <> +std::string LengthModFor<int>() { + return ""; +} +template <> +std::string LengthModFor<unsigned>() { + return ""; +} +template <> +std::string LengthModFor<long>() { // NOLINT + return "l"; +} +template <> +std::string LengthModFor<unsigned long>() { // NOLINT + return "l"; +} +template <> +std::string LengthModFor<long long>() { // NOLINT + return "ll"; +} +template <> +std::string LengthModFor<unsigned long long>() { // NOLINT + return "ll"; +} + +// An integral type of the same rank and signedness as `wchar_t`, that isn't +// `wchar_t`. +using IntegralTypeForWCharT = + std::conditional_t<std::is_signed<wchar_t>::value, + // Some STLs are broken and return `wchar_t` from + // `std::make_[un]signed_t<wchar_t>` when the signedness + // matches. Work around by round-tripping through the + // opposite signedness. + std::make_signed_t<std::make_unsigned_t<wchar_t>>, + std::make_unsigned_t<std::make_signed_t<wchar_t>>>; + +// Given an integral type `T`, returns a type of the same rank and signedness +// that is guaranteed to not be `wchar_t`. +template <typename T> +using MatchingIntegralType = std::conditional_t<std::is_same<T, wchar_t>::value, + IntegralTypeForWCharT, T>; std::string EscCharImpl(int v) { - if (std::isprint(static_cast<unsigned char>(v))) { - return std::string(1, static_cast<char>(v)); - } char buf[64]; - int n = snprintf(buf, sizeof(buf), "\\%#.2x", - static_cast<unsigned>(v & 0xff)); - assert(n > 0 && n < sizeof(buf)); - return std::string(buf, n); + int n = absl::ascii_isprint(static_cast<unsigned char>(v)) + ? snprintf(buf, sizeof(buf), "'%c'", v) + : snprintf(buf, sizeof(buf), "'\\x%.*x'", CHAR_BIT / 4, + static_cast<unsigned>( + static_cast<std::make_unsigned_t<char>>(v))); + assert(n > 0 && static_cast<size_t>(n) < sizeof(buf)); + return std::string(buf, static_cast<size_t>(n)); } std::string Esc(char v) { return EscCharImpl(v); } std::string Esc(signed char v) { return EscCharImpl(v); } std::string Esc(unsigned char v) { return EscCharImpl(v); } +std::string Esc(wchar_t v) { + char buf[64]; + int n = std::iswprint(static_cast<wint_t>(v)) + ? snprintf(buf, sizeof(buf), "L'%lc'", static_cast<wint_t>(v)) + : snprintf(buf, sizeof(buf), "L'\\x%.*llx'", + static_cast<int>(sizeof(wchar_t) * CHAR_BIT / 4), + static_cast<unsigned long long>( + static_cast<std::make_unsigned_t<wchar_t>>(v))); + assert(n > 0 && static_cast<size_t>(n) < sizeof(buf)); + return std::string(buf, static_cast<size_t>(n)); +} + template <typename T> std::string Esc(const T &v) { std::ostringstream oss; @@ -101,7 +186,7 @@ if (result < kSpaceLength) { if (result >= 0) { // Normal case -- everything fit. - dst->append(space, result); + dst->append(space, static_cast<size_t>(result)); return; } if (result < 0) { @@ -112,7 +197,7 @@ // Increase the buffer size to the size requested by vsnprintf, // plus one for the closing \0. - int length = result + 1; + size_t length = static_cast<size_t>(result) + 1; char *buf = new char[length]; // Restore the va_list before we use it again @@ -120,9 +205,9 @@ result = vsnprintf(buf, length, format, backup_ap); va_end(backup_ap); - if (result >= 0 && result < length) { + if (result >= 0 && static_cast<size_t>(result) < length) { // It fit - dst->append(buf, result); + dst->append(buf, static_cast<size_t>(result)); } delete[] buf; } @@ -231,11 +316,15 @@ TEST_F(FormatConvertTest, BasicString) { TestStringConvert("hello"); // As char array. + TestStringConvert(L"hello"); TestStringConvert(static_cast<const char*>("hello")); + TestStringConvert(static_cast<const wchar_t*>(L"hello")); TestStringConvert(std::string("hello")); + TestStringConvert(std::wstring(L"hello")); TestStringConvert(string_view("hello")); #if defined(ABSL_HAVE_STD_STRING_VIEW) TestStringConvert(std::string_view("hello")); + TestStringConvert(std::wstring_view(L"hello")); #endif // ABSL_HAVE_STD_STRING_VIEW } @@ -243,6 +332,10 @@ const char* p = nullptr; UntypedFormatSpecImpl format("%s"); EXPECT_EQ("", FormatPack(format, {FormatArgImpl(p)})); + + const wchar_t* wp = nullptr; + UntypedFormatSpecImpl wformat("%ls"); + EXPECT_EQ("", FormatPack(wformat, {FormatArgImpl(wp)})); } TEST_F(FormatConvertTest, StringPrecision) { @@ -252,10 +345,19 @@ UntypedFormatSpecImpl format("%.1s"); EXPECT_EQ("a", FormatPack(format, {FormatArgImpl(p)})); + wchar_t wc = L'a'; + const wchar_t* wp = &wc; + UntypedFormatSpecImpl wformat("%.1ls"); + EXPECT_EQ("a", FormatPack(wformat, {FormatArgImpl(wp)})); + // We cap at the NUL-terminator. p = "ABC"; UntypedFormatSpecImpl format2("%.10s"); EXPECT_EQ("ABC", FormatPack(format2, {FormatArgImpl(p)})); + + wp = L"ABC"; + UntypedFormatSpecImpl wformat2("%.10ls"); + EXPECT_EQ("ABC", FormatPack(wformat2, {FormatArgImpl(wp)})); } // Pointer formatting is implementation defined. This checks that the argument @@ -278,16 +380,25 @@ char *mcp = &c; const char *cp = "hi"; const char *cnil = nullptr; + wchar_t wc = L'h'; + wchar_t *mwcp = &wc; + const wchar_t *wcp = L"hi"; + const wchar_t *wcnil = nullptr; const int *inil = nullptr; using VoidF = void (*)(); VoidF fp = [] {}, fnil = nullptr; volatile char vc; volatile char *vcp = &vc; volatile char *vcnil = nullptr; + volatile wchar_t vwc; + volatile wchar_t *vwcp = &vwc; + volatile wchar_t *vwcnil = nullptr; const FormatArgImpl args_array[] = { - FormatArgImpl(xp), FormatArgImpl(cp), FormatArgImpl(inil), - FormatArgImpl(cnil), FormatArgImpl(mcp), FormatArgImpl(fp), - FormatArgImpl(fnil), FormatArgImpl(vcp), FormatArgImpl(vcnil), + FormatArgImpl(xp), FormatArgImpl(cp), FormatArgImpl(wcp), + FormatArgImpl(inil), FormatArgImpl(cnil), FormatArgImpl(wcnil), + FormatArgImpl(mcp), FormatArgImpl(mwcp), FormatArgImpl(fp), + FormatArgImpl(fnil), FormatArgImpl(vcp), FormatArgImpl(vwcp), + FormatArgImpl(vcnil), FormatArgImpl(vwcnil), }; auto args = absl::MakeConstSpan(args_array); @@ -313,30 +424,49 @@ EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%-30.20p"), args), MatchesPointerString(&x)); + // const int* + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%1$p"), args), + MatchesPointerString(xp)); // const char* EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%2$p"), args), MatchesPointerString(cp)); - // null const int* + // const wchar_t* EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%3$p"), args), - MatchesPointerString(nullptr)); - // null const char* + MatchesPointerString(wcp)); + // null const int* EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%4$p"), args), MatchesPointerString(nullptr)); - // nonconst char* + // null const char* EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%5$p"), args), - MatchesPointerString(mcp)); - - // function pointers - EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%6$p"), args), - MatchesPointerString(reinterpret_cast<const void*>(fp))); - EXPECT_THAT( - FormatPack(UntypedFormatSpecImpl("%8$p"), args), - MatchesPointerString(reinterpret_cast<volatile const void *>(vcp))); - - // null function pointers - EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%7$p"), args), MatchesPointerString(nullptr)); + // null const wchar_t* + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%6$p"), args), + MatchesPointerString(nullptr)); + // nonconst char* + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%7$p"), args), + MatchesPointerString(mcp)); + // nonconst wchar_t* + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%8$p"), args), + MatchesPointerString(mwcp)); + // function pointer EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%9$p"), args), + MatchesPointerString(reinterpret_cast<const void *>(fp))); + // null function pointer + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%10$p"), args), + MatchesPointerString(nullptr)); + // volatile char* + EXPECT_THAT( + FormatPack(UntypedFormatSpecImpl("%11$p"), args), + MatchesPointerString(reinterpret_cast<volatile const void *>(vcp))); + // volatile wchar_t* + EXPECT_THAT( + FormatPack(UntypedFormatSpecImpl("%12$p"), args), + MatchesPointerString(reinterpret_cast<volatile const void *>(vwcp))); + // null volatile char* + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%13$p"), args), + MatchesPointerString(nullptr)); + // null volatile wchar_t* + EXPECT_THAT(FormatPack(UntypedFormatSpecImpl("%14$p"), args), MatchesPointerString(nullptr)); } @@ -436,12 +566,15 @@ // as printf can't do that conversion properly. For those // cases, we do expect agreement with printf with a "%u" // and the unsigned equivalent of 'val'. - UnsignedT uval = val; - old_fmt += LengthModFor(uval); + UnsignedT uval = + static_cast<std::remove_volatile_t<UnsignedT>>(val); + old_fmt += LengthModFor< + MatchingIntegralType<std::remove_cv_t<decltype(uval)>>>(); old_fmt += "u"; old_result = StrPrint(old_fmt.c_str(), uval); } else { - old_fmt += LengthModFor(val); + old_fmt += LengthModFor< + MatchingIntegralType<std::remove_cv_t<decltype(val)>>>(); old_fmt += conv_char; old_result = StrPrint(old_fmt.c_str(), val); } @@ -459,6 +592,47 @@ } } +template <typename T> +absl::optional<std::string> StrPrintChar(T c) { + return StrPrint("%c", static_cast<int>(c)); +} +template <> +absl::optional<std::string> StrPrintChar(wchar_t c) { + // musl libc has a bug where ("%lc", 0) writes no characters, and Android + // doesn't support forcing UTF-8 via setlocale(). Hardcode the expected + // answers for ASCII inputs to maximize test coverage on these platforms. + if (static_cast<std::make_unsigned_t<wchar_t>>(c) < 0x80) { + return std::string(1, static_cast<char>(c)); + } + + // Force a UTF-8 locale to match the expected `StrFormat()` behavior. + // It's important to copy the string returned by `old_locale` here, because + // its contents are not guaranteed to be valid after the next `setlocale()` + // call. + std::string old_locale = setlocale(LC_CTYPE, nullptr); + if (!setlocale(LC_CTYPE, "en_US.UTF-8")) { + return absl::nullopt; + } + const std::string output = StrPrint("%lc", static_cast<wint_t>(c)); + setlocale(LC_CTYPE, old_locale.c_str()); + return output; +} + +template <typename T> +typename std::remove_volatile<T>::type GetMaxForConversion() { + return static_cast<typename std::remove_volatile<T>::type>( + std::numeric_limits<int>::max()); +} + +template <> +wchar_t GetMaxForConversion<wchar_t>() { + // Don't return values that aren't legal Unicode. For wchar_t conversions in a + // UTF-8 locale, conversion behavior for such values is unspecified, and we + // don't care about matching it. + return (sizeof(wchar_t) * CHAR_BIT <= 16) ? wchar_t{0xffff} + : static_cast<wchar_t>(0x10ffff); +} + TYPED_TEST_P(TypedFormatConvertTest, Char) { // Pass a bunch of values of type TypeParam to both FormatPack and libc's // vsnprintf("%c", ...) (wrapped in StrPrint) to make sure we get the same @@ -475,28 +649,50 @@ // std::numeric_limits::max(), too, but vsnprintf("%c", ...) can't handle // anything larger than an int. Add in the most extreme values we can without // exceeding that range. + // Special case: Formatting a wchar_t should behave like vsnprintf("%lc"). + // Technically vsnprintf can accept a wint_t in this case, but since we must + // pass a wchar_t to FormatPack, the largest type we can use here is wchar_t. + using ArgType = + std::conditional_t<std::is_same<T, wchar_t>::value, wchar_t, int>; static const T kMin = - static_cast<remove_volatile_t>(std::numeric_limits<int>::min()); - static const T kMax = - static_cast<remove_volatile_t>(std::numeric_limits<int>::max()); - vals.insert(vals.end(), {kMin + 1, kMin, kMax - 1, kMax}); + static_cast<remove_volatile_t>(std::numeric_limits<ArgType>::min()); + static const T kMax = GetMaxForConversion<T>(); + vals.insert(vals.end(), {static_cast<remove_volatile_t>(kMin + 1), kMin, + static_cast<remove_volatile_t>(kMax - 1), kMax}); + static const auto kMaxWCharT = + static_cast<remove_volatile_t>(GetMaxForConversion<wchar_t>()); for (const T c : vals) { + SCOPED_TRACE(Esc(c)); const FormatArgImpl args[] = {FormatArgImpl(c)}; UntypedFormatSpecImpl format("%c"); - EXPECT_EQ(StrPrint("%c", static_cast<int>(c)), - FormatPack(format, absl::MakeSpan(args))); + absl::optional<std::string> result = StrPrintChar(c); + if (result.has_value()) { + EXPECT_EQ(result.value(), FormatPack(format, absl::MakeSpan(args))); + } + + // Also test that if the format specifier is "%lc", the argument is treated + // as if it's a `wchar_t`. + const T wc = + std::max(remove_volatile_t{0}, + std::min(static_cast<remove_volatile_t>(c), kMaxWCharT)); + SCOPED_TRACE(Esc(wc)); + const FormatArgImpl wide_args[] = {FormatArgImpl(wc)}; + UntypedFormatSpecImpl wide_format("%lc"); + result = StrPrintChar(static_cast<wchar_t>(wc)); + if (result.has_value()) { + EXPECT_EQ(result.value(), + FormatPack(wide_format, absl::MakeSpan(wide_args))); + } } } REGISTER_TYPED_TEST_SUITE_P(TypedFormatConvertTest, AllIntsWithFlags, Char); -typedef ::testing::Types< - int, unsigned, volatile int, - short, unsigned short, - long, unsigned long, - long long, unsigned long long, - signed char, unsigned char, char> +typedef ::testing::Types<int, unsigned, volatile int, short, // NOLINT + unsigned short, long, unsigned long, // NOLINT + long long, unsigned long long, // NOLINT + signed char, unsigned char, char, wchar_t> AllIntTypes; INSTANTIATE_TYPED_TEST_SUITE_P(TypedFormatConvertTestWithAllIntTypes, TypedFormatConvertTest, AllIntTypes); @@ -511,6 +707,22 @@ FormatArgImpl(cv[0]), FormatArgImpl(cv[1])}))); } +TEST_F(FormatConvertTest, UnicodeWideString) { + // StrFormat() should be able to convert wide strings containing Unicode + // characters (to UTF-8). + const FormatArgImpl args[] = {FormatArgImpl(L"\u47e3 \U00011112")}; + // `u8""` forces UTF-8 encoding; MSVC will default to e.g. CP1252 (and warn) + // without it. However, the resulting character type differs between pre-C++20 + // (`char`) and C++20 (`char8_t`). So deduce the right character type for all + // C++ versions, init it with UTF-8, then `memcpy()` to get the result as a + // `char*`. + using ConstChar8T = std::remove_reference_t<decltype(*u8"a")>; + ConstChar8T kOutputUtf8[] = u8"\u47e3 \U00011112"; + char output[sizeof kOutputUtf8]; + std::memcpy(output, kOutputUtf8, sizeof kOutputUtf8); + EXPECT_EQ(output, + FormatPack(UntypedFormatSpecImpl("%ls"), absl::MakeSpan(args))); +} TEST_F(FormatConvertTest, Int128) { absl::int128 positive = static_cast<absl::int128>(0x1234567890abcdef) * 1979; @@ -1068,7 +1280,7 @@ // 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) {} + friend void AbslFormatFlush(NullSink *, string_view) {} }; template <typename... T>
diff --git a/absl/strings/internal/str_format/extension.h b/absl/strings/internal/str_format/extension.h index 8de42d2..173284c 100644 --- a/absl/strings/internal/str_format/extension.h +++ b/absl/strings/internal/str_format/extension.h
@@ -16,16 +16,14 @@ #ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_EXTENSION_H_ #define ABSL_STRINGS_INTERNAL_STR_FORMAT_EXTENSION_H_ -#include <limits.h> #include <cstddef> #include <cstdint> #include <cstring> #include <ostream> +#include <string> #include "absl/base/config.h" -#include "absl/base/port.h" -#include "absl/meta/type_traits.h" #include "absl/strings/internal/str_format/output.h" #include "absl/strings/string_view.h" @@ -34,6 +32,7 @@ enum class FormatConversionChar : uint8_t; enum class FormatConversionCharSet : uint64_t; +enum class LengthMod : std::uint8_t { h, hh, l, ll, L, j, z, t, q, none }; namespace str_format_internal { @@ -139,7 +138,8 @@ kAlt = 1 << 3, kZero = 1 << 4, // This is not a real flag. It just exists to turn off kBasic when no other - // flags are set. This is for when width/precision are specified. + // flags are set. This is for when width/precision are specified, or a length + // modifier affects the behavior ("%lc"). kNonBasic = 1 << 5, }; @@ -285,6 +285,8 @@ bool has_alt_flag() const { return FlagsContains(flags_, Flags::kAlt); } bool has_zero_flag() const { return FlagsContains(flags_, Flags::kZero); } + LengthMod length_mod() const { return length_mod_; } + 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. @@ -310,6 +312,7 @@ friend struct str_format_internal::FormatConversionSpecImplFriend; FormatConversionChar conv_ = FormatConversionCharInternal::kNone; Flags flags_; + LengthMod length_mod_ = LengthMod::none; int width_; int precision_; }; @@ -318,6 +321,9 @@ static void SetFlags(Flags f, FormatConversionSpecImpl* conv) { conv->flags_ = f; } + static void SetLengthMod(LengthMod l, FormatConversionSpecImpl* conv) { + conv->length_mod_ = l; + } static void SetConversionChar(FormatConversionChar c, FormatConversionSpecImpl* conv) { conv->conv_ = c;
diff --git a/absl/strings/internal/str_format/parser.h b/absl/strings/internal/str_format/parser.h index 35b6d49..b1d6d5f 100644 --- a/absl/strings/internal/str_format/parser.h +++ b/absl/strings/internal/str_format/parser.h
@@ -15,22 +15,23 @@ #ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_PARSER_H_ #define ABSL_STRINGS_INTERNAL_STR_FORMAT_PARSER_H_ -#include <limits.h> #include <stddef.h> #include <stdlib.h> #include <cassert> -#include <cstdint> +#include <cstring> #include <initializer_list> -#include <iosfwd> -#include <iterator> #include <memory> #include <string> +#include <utility> #include <vector> +#include "absl/base/config.h" +#include "absl/base/optimization.h" #include "absl/strings/internal/str_format/checker.h" #include "absl/strings/internal/str_format/constexpr_parser.h" #include "absl/strings/internal/str_format/extension.h" +#include "absl/strings/string_view.h" namespace absl { ABSL_NAMESPACE_BEGIN
diff --git a/absl/strings/internal/str_format/parser_test.cc b/absl/strings/internal/str_format/parser_test.cc index 021f6a8..e2225c6 100644 --- a/absl/strings/internal/str_format/parser_test.cc +++ b/absl/strings/internal/str_format/parser_test.cc
@@ -15,10 +15,18 @@ #include "absl/strings/internal/str_format/parser.h" #include <string.h> +#include <algorithm> +#include <initializer_list> +#include <string> +#include <utility> #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "absl/base/config.h" #include "absl/base/macros.h" +#include "absl/strings/internal/str_format/constexpr_parser.h" +#include "absl/strings/internal/str_format/extension.h" +#include "absl/strings/string_view.h" namespace absl { ABSL_NAMESPACE_BEGIN @@ -303,7 +311,7 @@ } // Flag is off - for (const char* fmt : {"3d", ".llx", "-G", "1$#X"}) { + for (const char* fmt : {"3d", ".llx", "-G", "1$#X", "lc"}) { SCOPED_TRACE(fmt); EXPECT_TRUE(Run(fmt)); EXPECT_NE(o.flags, Flags::kBasic);
diff --git a/absl/strings/numbers.cc b/absl/strings/numbers.cc index c4f2103..b57d9e8 100644 --- a/absl/strings/numbers.cc +++ b/absl/strings/numbers.cc
@@ -34,6 +34,7 @@ #include "absl/base/config.h" #include "absl/base/internal/endian.h" #include "absl/base/internal/raw_logging.h" +#include "absl/base/nullability.h" #include "absl/base/optimization.h" #include "absl/numeric/bits.h" #include "absl/numeric/int128.h" @@ -45,7 +46,7 @@ namespace absl { ABSL_NAMESPACE_BEGIN -bool SimpleAtof(absl::string_view str, float* out) { +bool SimpleAtof(absl::string_view str, absl::Nonnull<float*> out) { *out = 0.0; str = StripAsciiWhitespace(str); // std::from_chars doesn't accept an initial +, but SimpleAtof does, so if one @@ -76,7 +77,7 @@ return true; } -bool SimpleAtod(absl::string_view str, double* out) { +bool SimpleAtod(absl::string_view str, absl::Nonnull<double*> out) { *out = 0.0; str = StripAsciiWhitespace(str); // std::from_chars doesn't accept an initial +, but SimpleAtod does, so if one @@ -107,7 +108,7 @@ return true; } -bool SimpleAtob(absl::string_view str, bool* out) { +bool SimpleAtob(absl::string_view str, absl::Nonnull<bool*> out) { ABSL_RAW_CHECK(out != nullptr, "Output pointer must not be nullptr."); if (EqualsIgnoreCase(str, "true") || EqualsIgnoreCase(str, "t") || EqualsIgnoreCase(str, "yes") || EqualsIgnoreCase(str, "y") || @@ -166,7 +167,7 @@ constexpr uint64_t kDivisionBy100Div = 1 << 20; // Encode functions write the ASCII output of input `n` to `out_str`. -inline char* EncodeHundred(uint32_t n, char* out_str) { +inline char* EncodeHundred(uint32_t n, absl::Nonnull<char*> out_str) { int num_digits = static_cast<int>(n - 10) >> 8; uint32_t div10 = (n * kDivisionBy10Mul) / kDivisionBy10Div; uint32_t mod10 = n - 10u * div10; @@ -176,7 +177,7 @@ return out_str + 2 + num_digits; } -inline char* EncodeTenThousand(uint32_t n, char* out_str) { +inline char* EncodeTenThousand(uint32_t n, absl::Nonnull<char*> out_str) { // We split lower 2 digits and upper 2 digits of n into 2 byte consecutive // blocks. 123 -> [\0\1][\0\23]. We divide by 10 both blocks // (it's 1 division + zeroing upper bits), and compute modulo 10 as well "in @@ -232,8 +233,8 @@ return tens; } -inline ABSL_ATTRIBUTE_ALWAYS_INLINE char* EncodeFullU32(uint32_t n, - char* out_str) { +inline ABSL_ATTRIBUTE_ALWAYS_INLINE absl::Nonnull<char*> EncodeFullU32( + uint32_t n, absl::Nonnull<char*> out_str) { if (n < 10) { *out_str = static_cast<char>('0' + n); return out_str + 1; @@ -282,7 +283,7 @@ } // namespace -void numbers_internal::PutTwoDigits(uint32_t i, char* buf) { +void numbers_internal::PutTwoDigits(uint32_t i, absl::Nonnull<char*> buf) { assert(i < 100); uint32_t base = kTwoZeroBytes; uint32_t div10 = (i * kDivisionBy10Mul) / kDivisionBy10Div; @@ -291,13 +292,15 @@ little_endian::Store16(buf, static_cast<uint16_t>(base)); } -char* numbers_internal::FastIntToBuffer(uint32_t n, char* out_str) { +absl::Nonnull<char*> numbers_internal::FastIntToBuffer( + uint32_t n, absl::Nonnull<char*> out_str) { out_str = EncodeFullU32(n, out_str); *out_str = '\0'; return out_str; } -char* numbers_internal::FastIntToBuffer(int32_t i, char* buffer) { +absl::Nonnull<char*> numbers_internal::FastIntToBuffer( + int32_t i, absl::Nonnull<char*> buffer) { uint32_t u = static_cast<uint32_t>(i); if (i < 0) { *buffer++ = '-'; @@ -311,13 +314,15 @@ return buffer; } -char* numbers_internal::FastIntToBuffer(uint64_t i, char* buffer) { +absl::Nonnull<char*> numbers_internal::FastIntToBuffer( + uint64_t i, absl::Nonnull<char*> buffer) { buffer = EncodeFullU64(i, buffer); *buffer = '\0'; return buffer; } -char* numbers_internal::FastIntToBuffer(int64_t i, char* buffer) { +absl::Nonnull<char*> numbers_internal::FastIntToBuffer( + int64_t i, absl::Nonnull<char*> buffer) { uint64_t u = static_cast<uint64_t>(i); if (i < 0) { *buffer++ = '-'; @@ -538,7 +543,8 @@ // Helper function for fast formatting of floating-point. // The result is the same as "%g", a.k.a. "%.6g". -size_t numbers_internal::SixDigitsToBuffer(double d, char* const buffer) { +size_t numbers_internal::SixDigitsToBuffer(double d, + absl::Nonnull<char*> const buffer) { static_assert(std::numeric_limits<float>::is_iec559, "IEEE-754/IEC-559 support only"); @@ -685,9 +691,10 @@ 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36}; // Parse the sign and optional hex or oct prefix in text. -inline bool safe_parse_sign_and_base(absl::string_view* text /*inout*/, - int* base_ptr /*inout*/, - bool* negative_ptr /*output*/) { +inline bool safe_parse_sign_and_base( + absl::Nonnull<absl::string_view*> text /*inout*/, + absl::Nonnull<int*> base_ptr /*inout*/, + absl::Nonnull<bool*> negative_ptr /*output*/) { if (text->data() == nullptr) { return false; } @@ -972,7 +979,7 @@ template <typename IntType> inline bool safe_parse_positive_int(absl::string_view text, int base, - IntType* value_p) { + absl::Nonnull<IntType*> value_p) { IntType value = 0; const IntType vmax = std::numeric_limits<IntType>::max(); assert(vmax > 0); @@ -1009,7 +1016,7 @@ template <typename IntType> inline bool safe_parse_negative_int(absl::string_view text, int base, - IntType* value_p) { + absl::Nonnull<IntType*> value_p) { IntType value = 0; const IntType vmin = std::numeric_limits<IntType>::min(); assert(vmin < 0); @@ -1053,8 +1060,8 @@ // Input format based on POSIX.1-2008 strtol // http://pubs.opengroup.org/onlinepubs/9699919799/functions/strtol.html template <typename IntType> -inline bool safe_int_internal(absl::string_view text, IntType* value_p, - int base) { +inline bool safe_int_internal(absl::string_view text, + absl::Nonnull<IntType*> value_p, int base) { *value_p = 0; bool negative; if (!safe_parse_sign_and_base(&text, &base, &negative)) { @@ -1068,8 +1075,8 @@ } template <typename IntType> -inline bool safe_uint_internal(absl::string_view text, IntType* value_p, - int base) { +inline bool safe_uint_internal(absl::string_view text, + absl::Nonnull<IntType*> value_p, int base) { *value_p = 0; bool negative; if (!safe_parse_sign_and_base(&text, &base, &negative) || negative) { @@ -1103,27 +1110,33 @@ "e0e1e2e3e4e5e6e7e8e9eaebecedeeef" "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"; -bool safe_strto32_base(absl::string_view text, int32_t* value, int base) { +bool safe_strto32_base(absl::string_view text, absl::Nonnull<int32_t*> value, + int base) { return safe_int_internal<int32_t>(text, value, base); } -bool safe_strto64_base(absl::string_view text, int64_t* value, int base) { +bool safe_strto64_base(absl::string_view text, absl::Nonnull<int64_t*> value, + int base) { return safe_int_internal<int64_t>(text, value, base); } -bool safe_strto128_base(absl::string_view text, int128* value, int base) { +bool safe_strto128_base(absl::string_view text, absl::Nonnull<int128*> value, + int base) { return safe_int_internal<absl::int128>(text, value, base); } -bool safe_strtou32_base(absl::string_view text, uint32_t* value, int base) { +bool safe_strtou32_base(absl::string_view text, absl::Nonnull<uint32_t*> value, + int base) { return safe_uint_internal<uint32_t>(text, value, base); } -bool safe_strtou64_base(absl::string_view text, uint64_t* value, int base) { +bool safe_strtou64_base(absl::string_view text, absl::Nonnull<uint64_t*> value, + int base) { return safe_uint_internal<uint64_t>(text, value, base); } -bool safe_strtou128_base(absl::string_view text, uint128* value, int base) { +bool safe_strtou128_base(absl::string_view text, absl::Nonnull<uint128*> value, + int base) { return safe_uint_internal<absl::uint128>(text, value, base); }
diff --git a/absl/strings/numbers.h b/absl/strings/numbers.h index d7630ce..1d929de 100644 --- a/absl/strings/numbers.h +++ b/absl/strings/numbers.h
@@ -42,6 +42,7 @@ #include "absl/base/config.h" #include "absl/base/internal/endian.h" #include "absl/base/macros.h" +#include "absl/base/nullability.h" #include "absl/base/port.h" #include "absl/numeric/bits.h" #include "absl/numeric/int128.h" @@ -59,7 +60,8 @@ // encountered, this function returns `false`, leaving `out` in an unspecified // state. template <typename int_type> -ABSL_MUST_USE_RESULT bool SimpleAtoi(absl::string_view str, int_type* out); +ABSL_MUST_USE_RESULT bool SimpleAtoi(absl::string_view str, + absl::Nonnull<int_type*> out); // SimpleAtof() // @@ -70,7 +72,8 @@ // allowed formats for `str`, except SimpleAtof() is locale-independent and will // always use the "C" locale. If any errors are encountered, this function // returns `false`, leaving `out` in an unspecified state. -ABSL_MUST_USE_RESULT bool SimpleAtof(absl::string_view str, float* out); +ABSL_MUST_USE_RESULT bool SimpleAtof(absl::string_view str, + absl::Nonnull<float*> out); // SimpleAtod() // @@ -81,7 +84,8 @@ // allowed formats for `str`, except SimpleAtod is locale-independent and will // always use the "C" locale. If any errors are encountered, this function // returns `false`, leaving `out` in an unspecified state. -ABSL_MUST_USE_RESULT bool SimpleAtod(absl::string_view str, double* out); +ABSL_MUST_USE_RESULT bool SimpleAtod(absl::string_view str, + absl::Nonnull<double*> out); // SimpleAtob() // @@ -91,7 +95,8 @@ // are interpreted as boolean `false`: "false", "f", "no", "n", "0". If any // errors are encountered, this function returns `false`, leaving `out` in an // unspecified state. -ABSL_MUST_USE_RESULT bool SimpleAtob(absl::string_view str, bool* out); +ABSL_MUST_USE_RESULT bool SimpleAtob(absl::string_view str, + absl::Nonnull<bool*> out); // SimpleHexAtoi() // @@ -104,13 +109,14 @@ // by this function. If any errors are encountered, this function returns // `false`, leaving `out` in an unspecified state. template <typename int_type> -ABSL_MUST_USE_RESULT bool SimpleHexAtoi(absl::string_view str, int_type* out); +ABSL_MUST_USE_RESULT bool SimpleHexAtoi(absl::string_view str, + absl::Nonnull<int_type*> out); // Overloads of SimpleHexAtoi() for 128 bit integers. -ABSL_MUST_USE_RESULT inline bool SimpleHexAtoi(absl::string_view str, - absl::int128* out); -ABSL_MUST_USE_RESULT inline bool SimpleHexAtoi(absl::string_view str, - absl::uint128* out); +ABSL_MUST_USE_RESULT inline bool SimpleHexAtoi( + absl::string_view str, absl::Nonnull<absl::int128*> out); +ABSL_MUST_USE_RESULT inline bool SimpleHexAtoi( + absl::string_view str, absl::Nonnull<absl::uint128*> out); ABSL_NAMESPACE_END } // namespace absl @@ -132,18 +138,22 @@ // PutTwoDigits(42, buf); // // buf[0] == '4' // // buf[1] == '2' -void PutTwoDigits(uint32_t i, char* buf); +void PutTwoDigits(uint32_t i, absl::Nonnull<char*> buf); // safe_strto?() functions for implementing SimpleAtoi() -bool safe_strto32_base(absl::string_view text, int32_t* value, int base); -bool safe_strto64_base(absl::string_view text, int64_t* value, int base); -bool safe_strto128_base(absl::string_view text, absl::int128* value, - int base); -bool safe_strtou32_base(absl::string_view text, uint32_t* value, int base); -bool safe_strtou64_base(absl::string_view text, uint64_t* value, int base); -bool safe_strtou128_base(absl::string_view text, absl::uint128* value, - int base); +bool safe_strto32_base(absl::string_view text, absl::Nonnull<int32_t*> value, + int base); +bool safe_strto64_base(absl::string_view text, absl::Nonnull<int64_t*> value, + int base); +bool safe_strto128_base(absl::string_view text, + absl::Nonnull<absl::int128*> value, int base); +bool safe_strtou32_base(absl::string_view text, absl::Nonnull<uint32_t*> value, + int base); +bool safe_strtou64_base(absl::string_view text, absl::Nonnull<uint64_t*> value, + int base); +bool safe_strtou128_base(absl::string_view text, + absl::Nonnull<absl::uint128*> value, int base); static const int kFastToBufferSize = 32; static const int kSixDigitsToBufferSize = 16; @@ -154,20 +164,20 @@ // outside the range 0.0001-999999 are output using scientific notation // (1.23456e+06). This routine is heavily optimized. // Required buffer size is `kSixDigitsToBufferSize`. -size_t SixDigitsToBuffer(double d, char* buffer); +size_t SixDigitsToBuffer(double d, absl::Nonnull<char*> buffer); // These functions are intended for speed. All functions take an output buffer // as an argument and return a pointer to the last byte they wrote, which is the // terminating '\0'. At most `kFastToBufferSize` bytes are written. -char* FastIntToBuffer(int32_t, char*); -char* FastIntToBuffer(uint32_t, char*); -char* FastIntToBuffer(int64_t, char*); -char* FastIntToBuffer(uint64_t, char*); +absl::Nonnull<char*> FastIntToBuffer(int32_t, absl::Nonnull<char*>); +absl::Nonnull<char*> FastIntToBuffer(uint32_t, absl::Nonnull<char*>); +absl::Nonnull<char*> FastIntToBuffer(int64_t, absl::Nonnull<char*>); +absl::Nonnull<char*> FastIntToBuffer(uint64_t, absl::Nonnull<char*>); // For enums and integer types that are not an exact match for the types above, // use templates to call the appropriate one of the four overloads above. template <typename int_type> -char* FastIntToBuffer(int_type i, char* buffer) { +absl::Nonnull<char*> FastIntToBuffer(int_type i, absl::Nonnull<char*> buffer) { static_assert(sizeof(i) <= 64 / 8, "FastIntToBuffer works only with 64-bit-or-less integers."); // TODO(jorg): This signed-ness check is used because it works correctly @@ -194,7 +204,8 @@ // Implementation of SimpleAtoi, generalized to support arbitrary base (used // with base different from 10 elsewhere in Abseil implementation). template <typename int_type> -ABSL_MUST_USE_RESULT bool safe_strtoi_base(absl::string_view s, int_type* out, +ABSL_MUST_USE_RESULT bool safe_strtoi_base(absl::string_view s, + absl::Nonnull<int_type*> out, int base) { static_assert(sizeof(*out) == 4 || sizeof(*out) == 8, "SimpleAtoi works only with 32-bit or 64-bit integers."); @@ -237,7 +248,7 @@ // without the terminating null character. Thus `out` must be of length >= 16. // Returns the number of non-pad digits of the output (it can never be zero // since 0 has one digit). -inline size_t FastHexToBufferZeroPad16(uint64_t val, char* out) { +inline size_t FastHexToBufferZeroPad16(uint64_t val, absl::Nonnull<char*> out) { #ifdef ABSL_INTERNAL_HAVE_SSSE3 uint64_t be = absl::big_endian::FromHost64(val); const auto kNibbleMask = _mm_set1_epi8(0xf); @@ -263,32 +274,34 @@ } // namespace numbers_internal template <typename int_type> -ABSL_MUST_USE_RESULT bool SimpleAtoi(absl::string_view str, int_type* out) { +ABSL_MUST_USE_RESULT bool SimpleAtoi(absl::string_view str, + absl::Nonnull<int_type*> out) { return numbers_internal::safe_strtoi_base(str, out, 10); } ABSL_MUST_USE_RESULT inline bool SimpleAtoi(absl::string_view str, - absl::int128* out) { + absl::Nonnull<absl::int128*> out) { return numbers_internal::safe_strto128_base(str, out, 10); } ABSL_MUST_USE_RESULT inline bool SimpleAtoi(absl::string_view str, - absl::uint128* out) { + absl::Nonnull<absl::uint128*> out) { return numbers_internal::safe_strtou128_base(str, out, 10); } template <typename int_type> -ABSL_MUST_USE_RESULT bool SimpleHexAtoi(absl::string_view str, int_type* out) { +ABSL_MUST_USE_RESULT bool SimpleHexAtoi(absl::string_view str, + absl::Nonnull<int_type*> out) { return numbers_internal::safe_strtoi_base(str, out, 16); } -ABSL_MUST_USE_RESULT inline bool SimpleHexAtoi(absl::string_view str, - absl::int128* out) { +ABSL_MUST_USE_RESULT inline bool SimpleHexAtoi( + absl::string_view str, absl::Nonnull<absl::int128*> out) { return numbers_internal::safe_strto128_base(str, out, 16); } -ABSL_MUST_USE_RESULT inline bool SimpleHexAtoi(absl::string_view str, - absl::uint128* out) { +ABSL_MUST_USE_RESULT inline bool SimpleHexAtoi( + absl::string_view str, absl::Nonnull<absl::uint128*> out) { return numbers_internal::safe_strtou128_base(str, out, 16); }
diff --git a/absl/strings/str_cat.cc b/absl/strings/str_cat.cc index 900dc69..e7f7052 100644 --- a/absl/strings/str_cat.cc +++ b/absl/strings/str_cat.cc
@@ -23,6 +23,7 @@ #include <string> #include "absl/base/config.h" +#include "absl/base/nullability.h" #include "absl/strings/internal/resize_uninitialized.h" #include "absl/strings/string_view.h" @@ -40,7 +41,8 @@ namespace { // Append is merely a version of memcpy that returns the address of the byte // after the area just overwritten. -inline char* Append(char* out, const AlphaNum& x) { +inline absl::Nonnull<char*> Append(absl::Nonnull<char*> out, + const AlphaNum& x) { // memcpy is allowed to overwrite arbitrary memory, so doing this after the // call would force an extra fetch of x.size(). char* after = out + x.size(); @@ -128,7 +130,7 @@ assert(((src).size() == 0) || \ (uintptr_t((src).data() - (dest).data()) > uintptr_t((dest).size()))) -void AppendPieces(std::string* dest, +void AppendPieces(absl::Nonnull<std::string*> dest, std::initializer_list<absl::string_view> pieces) { size_t old_size = dest->size(); size_t to_append = 0; @@ -152,7 +154,7 @@ } // namespace strings_internal -void StrAppend(std::string* dest, const AlphaNum& a) { +void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a) { ASSERT_NO_OVERLAP(*dest, a); std::string::size_type old_size = dest->size(); STLStringAppendUninitializedAmortized(dest, a.size()); @@ -162,7 +164,8 @@ assert(out == begin + dest->size()); } -void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b) { +void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a, + const AlphaNum& b) { ASSERT_NO_OVERLAP(*dest, a); ASSERT_NO_OVERLAP(*dest, b); std::string::size_type old_size = dest->size(); @@ -174,8 +177,8 @@ assert(out == begin + dest->size()); } -void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b, - const AlphaNum& c) { +void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a, + const AlphaNum& b, const AlphaNum& c) { ASSERT_NO_OVERLAP(*dest, a); ASSERT_NO_OVERLAP(*dest, b); ASSERT_NO_OVERLAP(*dest, c); @@ -189,8 +192,8 @@ assert(out == begin + dest->size()); } -void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b, - const AlphaNum& c, const AlphaNum& d) { +void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a, + const AlphaNum& b, const AlphaNum& c, const AlphaNum& d) { ASSERT_NO_OVERLAP(*dest, a); ASSERT_NO_OVERLAP(*dest, b); ASSERT_NO_OVERLAP(*dest, c);
diff --git a/absl/strings/str_cat.h b/absl/strings/str_cat.h index e782368..68637ce 100644 --- a/absl/strings/str_cat.h +++ b/absl/strings/str_cat.h
@@ -100,6 +100,7 @@ #include <vector> #include "absl/base/attributes.h" +#include "absl/base/nullability.h" #include "absl/base/port.h" #include "absl/meta/type_traits.h" #include "absl/strings/has_absl_stringify.h" @@ -206,7 +207,7 @@ !std::is_pointer<Int>::value>::type* = nullptr) : Hex(spec, static_cast<uint64_t>(v)) {} template <typename Pointee> - explicit Hex(Pointee* v, PadSpec spec = absl::kNoPad) + explicit Hex(absl::Nullable<Pointee*> v, PadSpec spec = absl::kNoPad) : Hex(spec, reinterpret_cast<uintptr_t>(v)) {} template <typename S> @@ -349,7 +350,7 @@ ABSL_ATTRIBUTE_LIFETIME_BOUND) : piece_(&buf.data[0], buf.size) {} - AlphaNum(const char* c_str // NOLINT(runtime/explicit) + AlphaNum(absl::Nullable<const char*> c_str // NOLINT(runtime/explicit) ABSL_ATTRIBUTE_LIFETIME_BOUND) : piece_(NullSafeStringView(c_str)) {} AlphaNum(absl::string_view pc // NOLINT(runtime/explicit) @@ -376,7 +377,7 @@ AlphaNum& operator=(const AlphaNum&) = delete; absl::string_view::size_type size() const { return piece_.size(); } - const char* data() const { return piece_.data(); } + absl::Nullable<const char*> data() const { return piece_.data(); } absl::string_view Piece() const { return piece_; } // Match unscoped enums. Use integral promotion so that a `char`-backed @@ -446,7 +447,7 @@ // Do not call directly - this is not part of the public API. std::string CatPieces(std::initializer_list<absl::string_view> pieces); -void AppendPieces(std::string* dest, +void AppendPieces(absl::Nonnull<std::string*> dest, std::initializer_list<absl::string_view> pieces); template <typename Integer> @@ -576,19 +577,20 @@ // absl::string_view p = s; // StrAppend(&s, p); -inline void StrAppend(std::string*) {} -void StrAppend(std::string* dest, const AlphaNum& a); -void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b); -void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b, - const AlphaNum& c); -void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b, - const AlphaNum& c, const AlphaNum& d); +inline void StrAppend(absl::Nonnull<std::string*>) {} +void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a); +void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a, + const AlphaNum& b); +void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a, + const AlphaNum& b, const AlphaNum& c); +void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a, + const AlphaNum& b, const AlphaNum& c, const AlphaNum& d); // Support 5 or more arguments template <typename... AV> -inline void StrAppend(std::string* dest, const AlphaNum& a, const AlphaNum& b, - const AlphaNum& c, const AlphaNum& d, const AlphaNum& e, - const AV&... args) { +inline void StrAppend(absl::Nonnull<std::string*> dest, const AlphaNum& a, + const AlphaNum& b, const AlphaNum& c, const AlphaNum& d, + const AlphaNum& e, const AV&... args) { strings_internal::AppendPieces( dest, {a.Piece(), b.Piece(), c.Piece(), d.Piece(), e.Piece(), static_cast<const AlphaNum&>(args).Piece()...});
diff --git a/absl/strings/str_format.h b/absl/strings/str_format.h index 21ee179..66b6af5 100644 --- a/absl/strings/str_format.h +++ b/absl/strings/str_format.h
@@ -72,14 +72,21 @@ #ifndef ABSL_STRINGS_STR_FORMAT_H_ #define ABSL_STRINGS_STR_FORMAT_H_ +#include <cstdint> #include <cstdio> #include <string> +#include <type_traits> +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "absl/base/nullability.h" #include "absl/strings/internal/str_format/arg.h" // IWYU pragma: export #include "absl/strings/internal/str_format/bind.h" // IWYU pragma: export #include "absl/strings/internal/str_format/checker.h" // IWYU pragma: export #include "absl/strings/internal/str_format/extension.h" // IWYU pragma: export #include "absl/strings/internal/str_format/parser.h" // IWYU pragma: export +#include "absl/strings/string_view.h" +#include "absl/types/span.h" namespace absl { ABSL_NAMESPACE_BEGIN @@ -104,7 +111,8 @@ explicit UntypedFormatSpec(string_view s) : spec_(s) {} protected: - explicit UntypedFormatSpec(const str_format_internal::ParsedFormatBase* pc) + explicit UntypedFormatSpec( + absl::Nonnull<const str_format_internal::ParsedFormatBase*> pc) : spec_(pc) {} private: @@ -144,7 +152,7 @@ // EXPECT_EQ(8, n); class FormatCountCapture { public: - explicit FormatCountCapture(int* p) : p_(p) {} + explicit FormatCountCapture(absl::Nonnull<int*> p) : p_(p) {} private: // FormatCountCaptureHelper is used to define FormatConvertImpl() for this @@ -153,8 +161,8 @@ // Unused() is here because of the false positive from -Wunused-private-field // p_ is used in the templated function of the friend FormatCountCaptureHelper // class. - int* Unused() { return p_; } - int* p_; + absl::Nonnull<int*> Unused() { return p_; } + absl::Nonnull<int*> p_; }; // FormatSpec @@ -256,7 +264,7 @@ // // The `FormatSpec` intrinsically supports all of these fundamental C++ types: // -// * Characters: `char`, `signed char`, `unsigned char` +// * Characters: `char`, `signed char`, `unsigned char`, `wchar_t` // * Integers: `int`, `short`, `unsigned short`, `unsigned`, `long`, // `unsigned long`, `long long`, `unsigned long long` // * Enums: printed as their underlying integral value @@ -264,9 +272,9 @@ // // However, in the `str_format` library, a format conversion specifies a broader // C++ conceptual category instead of an exact type. For example, `%s` binds to -// any string-like argument, so `std::string`, `absl::string_view`, and -// `const char*` are all accepted. Likewise, `%d` accepts any integer-like -// argument, etc. +// any string-like argument, so `std::string`, `std::wstring`, +// `absl::string_view`, `const char*`, and `const wchar_t*` are all accepted. +// Likewise, `%d` accepts any integer-like argument, etc. template <typename... Args> using FormatSpec = str_format_internal::FormatSpecTemplate< @@ -369,7 +377,7 @@ // std::string orig("For example PI is approximately "); // std::cout << StrAppendFormat(&orig, "%12.6f", 3.14); template <typename... Args> -std::string& StrAppendFormat(std::string* dst, +std::string& StrAppendFormat(absl::Nonnull<std::string*> dst, const FormatSpec<Args...>& format, const Args&... args) { return str_format_internal::AppendPack( @@ -429,7 +437,7 @@ // Outputs: "The capital of Mongolia is Ulaanbaatar" // template <typename... Args> -int FPrintF(std::FILE* output, const FormatSpec<Args...>& format, +int FPrintF(absl::Nonnull<std::FILE*> output, const FormatSpec<Args...>& format, const Args&... args) { return str_format_internal::FprintF( output, str_format_internal::UntypedFormatSpecImpl::Extract(format), @@ -458,8 +466,8 @@ // Post-condition: output == "The capital of Mongolia is Ulaanbaatar" // template <typename... Args> -int SNPrintF(char* output, std::size_t size, const FormatSpec<Args...>& format, - const Args&... args) { +int SNPrintF(absl::Nonnull<char*> output, std::size_t size, + const FormatSpec<Args...>& format, const Args&... args) { return str_format_internal::SnprintF( output, size, str_format_internal::UntypedFormatSpecImpl::Extract(format), {str_format_internal::FormatArgImpl(args)...}); @@ -492,7 +500,7 @@ template <typename T, typename = typename std::enable_if<std::is_constructible< str_format_internal::FormatRawSinkImpl, T*>::value>::type> - FormatRawSink(T* raw) // NOLINT + FormatRawSink(absl::Nonnull<T*> raw) // NOLINT : sink_(raw) {} private: @@ -849,14 +857,16 @@ } // Support `absl::Format(&sink, format, args...)`. - friend void AbslFormatFlush(FormatSink* sink, absl::string_view v) { + friend void AbslFormatFlush(absl::Nonnull<FormatSink*> sink, + absl::string_view v) { sink->Append(v); } private: friend str_format_internal::FormatSinkImpl; - explicit FormatSink(str_format_internal::FormatSinkImpl* s) : sink_(s) {} - str_format_internal::FormatSinkImpl* sink_; + explicit FormatSink(absl::Nonnull<str_format_internal::FormatSinkImpl*> s) + : sink_(s) {} + absl::Nonnull<str_format_internal::FormatSinkImpl*> sink_; }; // FormatConvertResult
diff --git a/absl/strings/str_format_test.cc b/absl/strings/str_format_test.cc index 195ef3f..3c52be1 100644 --- a/absl/strings/str_format_test.cc +++ b/absl/strings/str_format_test.cc
@@ -634,6 +634,10 @@ const int& something = *reinterpret_cast<const int*>(ptr_value); EXPECT_EQ(StrFormat("%p", &something), StrFormat("0x%x", ptr_value)); + // The output of formatting a null pointer is not documented as being a + // specific thing, but the attempt should at least compile. + (void)StrFormat("%p", nullptr); + // Output widths are supported, with optional flags. EXPECT_EQ(StrFormat("%3d", 1), " 1"); EXPECT_EQ(StrFormat("%3d", 123456), "123456");
diff --git a/absl/strings/str_replace.cc b/absl/strings/str_replace.cc index 9ce49e5..a7ab52f 100644 --- a/absl/strings/str_replace.cc +++ b/absl/strings/str_replace.cc
@@ -21,6 +21,7 @@ #include <vector> #include "absl/base/config.h" +#include "absl/base/nullability.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" @@ -36,8 +37,8 @@ // occurred. int ApplySubstitutions( absl::string_view s, - std::vector<strings_internal::ViableSubstitution>* subs_ptr, - std::string* result_ptr) { + absl::Nonnull<std::vector<strings_internal::ViableSubstitution>*> subs_ptr, + absl::Nonnull<std::string*> result_ptr) { auto& subs = *subs_ptr; int substitutions = 0; size_t pos = 0; @@ -82,7 +83,7 @@ } int StrReplaceAll(strings_internal::FixedMapping replacements, - std::string* target) { + absl::Nonnull<std::string*> target) { return StrReplaceAll<strings_internal::FixedMapping>(replacements, target); }
diff --git a/absl/strings/str_replace.h b/absl/strings/str_replace.h index 273c707..e77ced3 100644 --- a/absl/strings/str_replace.h +++ b/absl/strings/str_replace.h
@@ -43,6 +43,7 @@ #include <vector> #include "absl/base/attributes.h" +#include "absl/base/nullability.h" #include "absl/strings/string_view.h" namespace absl { @@ -113,7 +114,7 @@ int StrReplaceAll( std::initializer_list<std::pair<absl::string_view, absl::string_view>> replacements, - std::string* target); + absl::Nonnull<std::string*> target); // Overload of `StrReplaceAll()` to replace patterns within a given output // string *in place* with replacements provided within a container of key/value @@ -128,7 +129,8 @@ // EXPECT_EQ(count, 2); // EXPECT_EQ("if (ptr < &foo)", s); template <typename StrToStrMapping> -int StrReplaceAll(const StrToStrMapping& replacements, std::string* target); +int StrReplaceAll(const StrToStrMapping& replacements, + absl::Nonnull<std::string*> target); // Implementation details only, past this point. namespace strings_internal { @@ -185,8 +187,8 @@ } int ApplySubstitutions(absl::string_view s, - std::vector<ViableSubstitution>* subs_ptr, - std::string* result_ptr); + absl::Nonnull<std::vector<ViableSubstitution>*> subs_ptr, + absl::Nonnull<std::string*> result_ptr); } // namespace strings_internal @@ -201,7 +203,8 @@ } template <typename StrToStrMapping> -int StrReplaceAll(const StrToStrMapping& replacements, std::string* target) { +int StrReplaceAll(const StrToStrMapping& replacements, + absl::Nonnull<std::string*> target) { auto subs = strings_internal::FindSubstitutions(*target, replacements); if (subs.empty()) return 0;
diff --git a/absl/strings/string_view.cc b/absl/strings/string_view.cc index 1ee468c..0500ef5 100644 --- a/absl/strings/string_view.cc +++ b/absl/strings/string_view.cc
@@ -21,6 +21,8 @@ #include <cstring> #include <ostream> +#include "absl/base/nullability.h" + namespace absl { ABSL_NAMESPACE_BEGIN @@ -28,8 +30,10 @@ // This is significantly faster for case-sensitive matches with very // few possible matches. -const char* memmatch(const char* phaystack, size_t haylen, const char* pneedle, - size_t neelen) { +absl::Nullable<const char*> memmatch(absl::Nullable<const char*> phaystack, + size_t haylen, + absl::Nullable<const char*> pneedle, + size_t neelen) { if (0 == neelen) { return phaystack; // even if haylen is 0 }
diff --git a/absl/strings/string_view.h b/absl/strings/string_view.h index f88b05b..04ca0a3 100644 --- a/absl/strings/string_view.h +++ b/absl/strings/string_view.h
@@ -37,6 +37,7 @@ #include <string> #include "absl/base/attributes.h" +#include "absl/base/nullability.h" #include "absl/base/config.h" #include "absl/base/internal/throw_delegate.h" #include "absl/base/macros.h" @@ -162,11 +163,11 @@ public: using traits_type = std::char_traits<char>; using value_type = char; - using pointer = char*; - using const_pointer = const char*; + using pointer = absl::Nullable<char*>; + using const_pointer = absl::Nullable<const char*>; using reference = char&; using const_reference = const char&; - using const_iterator = const char*; + using const_iterator = absl::Nullable<const char*>; using iterator = const_iterator; using const_reverse_iterator = std::reverse_iterator<const_iterator>; using reverse_iterator = const_reverse_iterator; @@ -194,11 +195,12 @@ // accepting possibly null strings, use `absl::NullSafeStringView(str)` // instead (see below). // The length check is skipped since it is unnecessary and causes code bloat. - constexpr string_view(const char* str) // NOLINT(runtime/explicit) + constexpr string_view( // NOLINT(runtime/explicit) + absl::Nonnull<const char*> str) : ptr_(str), length_(str ? StrlenInternal(str) : 0) {} // Implicit constructor of a `string_view` from a `const char*` and length. - constexpr string_view(const char* data, size_type len) + constexpr string_view(absl::Nullable<const char*> data, size_type len) : ptr_(data), length_(CheckLengthInternal(len)) {} // NOTE: Harmlessly omitted to work around gdb bug. @@ -427,18 +429,21 @@ // Overload of `string_view::compare()` for comparing a `string_view` and a // a different C-style string `s`. - constexpr int compare(const char* s) const { return compare(string_view(s)); } + constexpr int compare(absl::Nonnull<const char*> s) const { + return compare(string_view(s)); + } // Overload of `string_view::compare()` for comparing a substring of the // `string_view` and a different string C-style string `s`. - constexpr int compare(size_type pos1, size_type count1, const char* s) const { + constexpr int compare(size_type pos1, size_type count1, + absl::Nonnull<const char*> s) const { return substr(pos1, count1).compare(string_view(s)); } // Overload of `string_view::compare()` for comparing a substring of the // `string_view` and a substring of a different C-style string `s`. - constexpr int compare(size_type pos1, size_type count1, const char* s, - size_type count2) const { + constexpr int compare(size_type pos1, size_type count1, + absl::Nonnull<const char*> s, size_type count2) const { return substr(pos1, count1).compare(string_view(s, count2)); } @@ -457,13 +462,14 @@ // Overload of `string_view::find()` for finding a substring of a different // C-style string `s` within the `string_view`. - size_type find(const char* s, size_type pos, size_type count) const { + size_type find(absl::Nonnull<const char*> s, size_type pos, + size_type count) const { return find(string_view(s, count), pos); } // Overload of `string_view::find()` for finding a different C-style string // `s` within the `string_view`. - size_type find(const char* s, size_type pos = 0) const { + size_type find(absl::Nonnull<const char *> s, size_type pos = 0) const { return find(string_view(s), pos); } @@ -480,13 +486,14 @@ // Overload of `string_view::rfind()` for finding a substring of a different // C-style string `s` within the `string_view`. - size_type rfind(const char* s, size_type pos, size_type count) const { + size_type rfind(absl::Nonnull<const char*> s, size_type pos, + size_type count) const { return rfind(string_view(s, count), pos); } // Overload of `string_view::rfind()` for finding a different C-style string // `s` within the `string_view`. - size_type rfind(const char* s, size_type pos = npos) const { + size_type rfind(absl::Nonnull<const char*> s, size_type pos = npos) const { return rfind(string_view(s), pos); } @@ -505,14 +512,15 @@ // Overload of `string_view::find_first_of()` for finding a substring of a // different C-style string `s` within the `string_view`. - size_type find_first_of(const char* s, size_type pos, + size_type find_first_of(absl::Nonnull<const char*> s, size_type pos, size_type count) const { return find_first_of(string_view(s, count), pos); } // Overload of `string_view::find_first_of()` for finding a different C-style // string `s` within the `string_view`. - size_type find_first_of(const char* s, size_type pos = 0) const { + size_type find_first_of(absl::Nonnull<const char*> s, + size_type pos = 0) const { return find_first_of(string_view(s), pos); } @@ -531,13 +539,15 @@ // Overload of `string_view::find_last_of()` for finding a substring of a // different C-style string `s` within the `string_view`. - size_type find_last_of(const char* s, size_type pos, size_type count) const { + size_type find_last_of(absl::Nonnull<const char*> s, size_type pos, + size_type count) const { return find_last_of(string_view(s, count), pos); } // Overload of `string_view::find_last_of()` for finding a different C-style // string `s` within the `string_view`. - size_type find_last_of(const char* s, size_type pos = npos) const { + size_type find_last_of(absl::Nonnull<const char*> s, + size_type pos = npos) const { return find_last_of(string_view(s), pos); } @@ -554,14 +564,15 @@ // Overload of `string_view::find_first_not_of()` for finding a substring of a // different C-style string `s` within the `string_view`. - size_type find_first_not_of(const char* s, size_type pos, + size_type find_first_not_of(absl::Nonnull<const char*> s, size_type pos, size_type count) const { return find_first_not_of(string_view(s, count), pos); } // Overload of `string_view::find_first_not_of()` for finding a different // C-style string `s` within the `string_view`. - size_type find_first_not_of(const char* s, size_type pos = 0) const { + size_type find_first_not_of(absl::Nonnull<const char*> s, + size_type pos = 0) const { return find_first_not_of(string_view(s), pos); } @@ -579,14 +590,15 @@ // Overload of `string_view::find_last_not_of()` for finding a substring of a // different C-style string `s` within the `string_view`. - size_type find_last_not_of(const char* s, size_type pos, + size_type find_last_not_of(absl::Nonnull<const char*> s, size_type pos, size_type count) const { return find_last_not_of(string_view(s, count), pos); } // Overload of `string_view::find_last_not_of()` for finding a different // C-style string `s` within the `string_view`. - size_type find_last_not_of(const char* s, size_type pos = npos) const { + size_type find_last_not_of(absl::Nonnull<const char*> s, + size_type pos = npos) const { return find_last_not_of(string_view(s), pos); } @@ -646,7 +658,8 @@ // The constructor from std::string delegates to this constructor. // See the comment on that constructor for the rationale. struct SkipCheckLengthTag {}; - string_view(const char* data, size_type len, SkipCheckLengthTag) noexcept + string_view(absl::Nullable<const char*> data, size_type len, + SkipCheckLengthTag) noexcept : ptr_(data), length_(len) {} static constexpr size_type kMaxSize = @@ -656,7 +669,7 @@ return ABSL_HARDENING_ASSERT(len <= kMaxSize), len; } - static constexpr size_type StrlenInternal(const char* str) { + static constexpr size_type StrlenInternal(absl::Nonnull<const char*> str) { #if defined(_MSC_VER) && _MSC_VER >= 1910 && !defined(__clang__) // MSVC 2017+ can evaluate this at compile-time. const char* begin = str; @@ -685,7 +698,7 @@ : (compare_result < 0 ? -1 : 1); } - const char* ptr_; + absl::Nullable<const char*> ptr_; size_type length_; }; @@ -746,7 +759,7 @@ // Creates an `absl::string_view` from a pointer `p` even if it's null-valued. // This function should be used where an `absl::string_view` can be created from // a possibly-null pointer. -constexpr string_view NullSafeStringView(const char* p) { +constexpr string_view NullSafeStringView(absl::Nullable<const char*> p) { return p ? string_view(p) : string_view(); }
diff --git a/absl/strings/strip.h b/absl/strings/strip.h index 341e66f..e3cda5b 100644 --- a/absl/strings/strip.h +++ b/absl/strings/strip.h
@@ -25,6 +25,7 @@ #include <string> #include "absl/base/macros.h" +#include "absl/base/nullability.h" #include "absl/strings/ascii.h" #include "absl/strings/match.h" #include "absl/strings/string_view.h" @@ -43,7 +44,8 @@ // absl::string_view input("abc"); // EXPECT_TRUE(absl::ConsumePrefix(&input, "a")); // EXPECT_EQ(input, "bc"); -inline bool ConsumePrefix(absl::string_view* str, absl::string_view expected) { +inline bool ConsumePrefix(absl::Nonnull<absl::string_view*> str, + absl::string_view expected) { if (!absl::StartsWith(*str, expected)) return false; str->remove_prefix(expected.size()); return true; @@ -59,7 +61,8 @@ // absl::string_view input("abcdef"); // EXPECT_TRUE(absl::ConsumeSuffix(&input, "def")); // EXPECT_EQ(input, "abc"); -inline bool ConsumeSuffix(absl::string_view* str, absl::string_view expected) { +inline bool ConsumeSuffix(absl::Nonnull<absl::string_view*> str, + absl::string_view expected) { if (!absl::EndsWith(*str, expected)) return false; str->remove_suffix(expected.size()); return true;
diff --git a/absl/strings/substitute.cc b/absl/strings/substitute.cc index 354c070..dd32c75 100644 --- a/absl/strings/substitute.cc +++ b/absl/strings/substitute.cc
@@ -22,6 +22,7 @@ #include "absl/base/config.h" #include "absl/base/internal/raw_logging.h" +#include "absl/base/nullability.h" #include "absl/strings/ascii.h" #include "absl/strings/escaping.h" #include "absl/strings/internal/resize_uninitialized.h" @@ -33,9 +34,9 @@ ABSL_NAMESPACE_BEGIN namespace substitute_internal { -void SubstituteAndAppendArray(std::string* output, absl::string_view format, - const absl::string_view* args_array, - size_t num_args) { +void SubstituteAndAppendArray( + absl::Nonnull<std::string*> output, absl::string_view format, + absl::Nullable<const absl::string_view*> args_array, size_t num_args) { // Determine total size needed. size_t size = 0; for (size_t i = 0; i < format.size(); i++) { @@ -104,7 +105,7 @@ assert(target == output->data() + output->size()); } -Arg::Arg(const void* value) { +Arg::Arg(absl::Nullable<const void*> value) { static_assert(sizeof(scratch_) >= sizeof(value) * 2 + 2, "fix sizeof(scratch_)"); if (value == nullptr) {
diff --git a/absl/strings/substitute.h b/absl/strings/substitute.h index 0d6cb7c..6c7cba4 100644 --- a/absl/strings/substitute.h +++ b/absl/strings/substitute.h
@@ -78,6 +78,7 @@ #include <vector> #include "absl/base/macros.h" +#include "absl/base/nullability.h" #include "absl/base/port.h" #include "absl/strings/ascii.h" #include "absl/strings/escaping.h" @@ -105,7 +106,7 @@ // Overloads for string-y things // // Explicitly overload `const char*` so the compiler doesn't cast to `bool`. - Arg(const char* value) // NOLINT(google-explicit-constructor) + Arg(absl::Nullable<const char*> value) // NOLINT(google-explicit-constructor) : piece_(absl::NullSafeStringView(value)) {} template <typename Allocator> Arg( // NOLINT @@ -197,7 +198,8 @@ // `void*` values, with the exception of `char*`, are printed as // "0x<hex value>". However, in the case of `nullptr`, "NULL" is printed. - Arg(const void* value); // NOLINT(google-explicit-constructor) + Arg( // NOLINT(google-explicit-constructor) + absl::Nullable<const void*> value); // Normal enums are already handled by the integer formatters. // This overload matches only scoped enums. @@ -220,12 +222,12 @@ // Internal helper function. Don't call this from outside this implementation. // This interface may change without notice. -void SubstituteAndAppendArray(std::string* output, absl::string_view format, - const absl::string_view* args_array, - size_t num_args); +void SubstituteAndAppendArray( + absl::Nonnull<std::string*> output, absl::string_view format, + absl::Nullable<const absl::string_view*> args_array, size_t num_args); #if defined(ABSL_BAD_CALL_IF) -constexpr int CalculateOneBit(const char* format) { +constexpr int CalculateOneBit(absl::Nonnull<const char*> format) { // Returns: // * 2^N for '$N' when N is in [0-9] // * 0 for correct '$' escaping: '$$'. @@ -234,11 +236,11 @@ : (1 << (*format - '0')); } -constexpr const char* SkipNumber(const char* format) { +constexpr const char* SkipNumber(absl::Nonnull<const char*> format) { return !*format ? format : (format + 1); } -constexpr int PlaceholderBitmask(const char* format) { +constexpr int PlaceholderBitmask(absl::Nonnull<const char*> format) { return !*format ? 0 : *format != '$' ? PlaceholderBitmask(format + 1) @@ -271,18 +273,21 @@ // absl::SubstituteAndAppend(boilerplate, format, args...); // } // -inline void SubstituteAndAppend(std::string* output, absl::string_view format) { +inline void SubstituteAndAppend(absl::Nonnull<std::string*> output, + absl::string_view format) { substitute_internal::SubstituteAndAppendArray(output, format, nullptr, 0); } -inline void SubstituteAndAppend(std::string* output, absl::string_view format, +inline void SubstituteAndAppend(absl::Nonnull<std::string*> output, + absl::string_view format, const substitute_internal::Arg& a0) { const absl::string_view args[] = {a0.piece()}; substitute_internal::SubstituteAndAppendArray(output, format, args, ABSL_ARRAYSIZE(args)); } -inline void SubstituteAndAppend(std::string* output, absl::string_view format, +inline void SubstituteAndAppend(absl::Nonnull<std::string*> output, + absl::string_view format, const substitute_internal::Arg& a0, const substitute_internal::Arg& a1) { const absl::string_view args[] = {a0.piece(), a1.piece()}; @@ -290,7 +295,8 @@ ABSL_ARRAYSIZE(args)); } -inline void SubstituteAndAppend(std::string* output, absl::string_view format, +inline void SubstituteAndAppend(absl::Nonnull<std::string*> output, + absl::string_view format, const substitute_internal::Arg& a0, const substitute_internal::Arg& a1, const substitute_internal::Arg& a2) { @@ -299,7 +305,8 @@ ABSL_ARRAYSIZE(args)); } -inline void SubstituteAndAppend(std::string* output, absl::string_view format, +inline void SubstituteAndAppend(absl::Nonnull<std::string*> output, + absl::string_view format, const substitute_internal::Arg& a0, const substitute_internal::Arg& a1, const substitute_internal::Arg& a2, @@ -310,7 +317,8 @@ ABSL_ARRAYSIZE(args)); } -inline void SubstituteAndAppend(std::string* output, absl::string_view format, +inline void SubstituteAndAppend(absl::Nonnull<std::string*> output, + absl::string_view format, const substitute_internal::Arg& a0, const substitute_internal::Arg& a1, const substitute_internal::Arg& a2, @@ -322,27 +330,23 @@ ABSL_ARRAYSIZE(args)); } -inline void SubstituteAndAppend(std::string* output, absl::string_view format, - const substitute_internal::Arg& a0, - const substitute_internal::Arg& a1, - const substitute_internal::Arg& a2, - const substitute_internal::Arg& a3, - const substitute_internal::Arg& a4, - const substitute_internal::Arg& a5) { +inline void SubstituteAndAppend( + absl::Nonnull<std::string*> output, absl::string_view format, + const substitute_internal::Arg& a0, const substitute_internal::Arg& a1, + const substitute_internal::Arg& a2, const substitute_internal::Arg& a3, + const substitute_internal::Arg& a4, const substitute_internal::Arg& a5) { const absl::string_view args[] = {a0.piece(), a1.piece(), a2.piece(), a3.piece(), a4.piece(), a5.piece()}; substitute_internal::SubstituteAndAppendArray(output, format, args, ABSL_ARRAYSIZE(args)); } -inline void SubstituteAndAppend(std::string* output, absl::string_view format, - const substitute_internal::Arg& a0, - const substitute_internal::Arg& a1, - const substitute_internal::Arg& a2, - const substitute_internal::Arg& a3, - const substitute_internal::Arg& a4, - const substitute_internal::Arg& a5, - const substitute_internal::Arg& a6) { +inline void SubstituteAndAppend( + absl::Nonnull<std::string*> output, absl::string_view format, + const substitute_internal::Arg& a0, const substitute_internal::Arg& a1, + const substitute_internal::Arg& a2, const substitute_internal::Arg& a3, + const substitute_internal::Arg& a4, const substitute_internal::Arg& a5, + const substitute_internal::Arg& a6) { const absl::string_view args[] = {a0.piece(), a1.piece(), a2.piece(), a3.piece(), a4.piece(), a5.piece(), a6.piece()}; @@ -351,7 +355,7 @@ } inline void SubstituteAndAppend( - std::string* output, absl::string_view format, + absl::Nonnull<std::string*> output, absl::string_view format, const substitute_internal::Arg& a0, const substitute_internal::Arg& a1, const substitute_internal::Arg& a2, const substitute_internal::Arg& a3, const substitute_internal::Arg& a4, const substitute_internal::Arg& a5, @@ -364,7 +368,7 @@ } inline void SubstituteAndAppend( - std::string* output, absl::string_view format, + absl::Nonnull<std::string*> output, absl::string_view format, const substitute_internal::Arg& a0, const substitute_internal::Arg& a1, const substitute_internal::Arg& a2, const substitute_internal::Arg& a3, const substitute_internal::Arg& a4, const substitute_internal::Arg& a5, @@ -378,7 +382,7 @@ } inline void SubstituteAndAppend( - std::string* output, absl::string_view format, + absl::Nonnull<std::string*> output, absl::string_view format, const substitute_internal::Arg& a0, const substitute_internal::Arg& a1, const substitute_internal::Arg& a2, const substitute_internal::Arg& a3, const substitute_internal::Arg& a4, const substitute_internal::Arg& a5, @@ -394,14 +398,16 @@ #if defined(ABSL_BAD_CALL_IF) // This body of functions catches cases where the number of placeholders // doesn't match the number of data arguments. -void SubstituteAndAppend(std::string* output, const char* format) +void SubstituteAndAppend(absl::Nonnull<std::string*> output, + absl::Nonnull<const char*> format) ABSL_BAD_CALL_IF( substitute_internal::PlaceholderBitmask(format) != 0, "There were no substitution arguments " "but this format string either has a $[0-9] in it or contains " "an unescaped $ character (use $$ instead)"); -void SubstituteAndAppend(std::string* output, const char* format, +void SubstituteAndAppend(absl::Nonnull<std::string*> output, + absl::Nonnull<const char*> format, const substitute_internal::Arg& a0) ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 1, "There was 1 substitution argument given, but " @@ -409,7 +415,8 @@ "one of $1-$9, or contains an unescaped $ character (use " "$$ instead)"); -void SubstituteAndAppend(std::string* output, const char* format, +void SubstituteAndAppend(absl::Nonnull<std::string*> output, + absl::Nonnull<const char*> format, const substitute_internal::Arg& a0, const substitute_internal::Arg& a1) ABSL_BAD_CALL_IF( @@ -418,7 +425,8 @@ "missing its $0/$1, contains one of $2-$9, or contains an " "unescaped $ character (use $$ instead)"); -void SubstituteAndAppend(std::string* output, const char* format, +void SubstituteAndAppend(absl::Nonnull<std::string*> output, + absl::Nonnull<const char*> format, const substitute_internal::Arg& a0, const substitute_internal::Arg& a1, const substitute_internal::Arg& a2) @@ -428,7 +436,8 @@ "this format string is missing its $0/$1/$2, contains one of " "$3-$9, or contains an unescaped $ character (use $$ instead)"); -void SubstituteAndAppend(std::string* output, const char* format, +void SubstituteAndAppend(absl::Nonnull<std::string*> output, + absl::Nonnull<const char*> format, const substitute_internal::Arg& a0, const substitute_internal::Arg& a1, const substitute_internal::Arg& a2, @@ -439,7 +448,8 @@ "this format string is missing its $0-$3, contains one of " "$4-$9, or contains an unescaped $ character (use $$ instead)"); -void SubstituteAndAppend(std::string* output, const char* format, +void SubstituteAndAppend(absl::Nonnull<std::string*> output, + absl::Nonnull<const char*> format, const substitute_internal::Arg& a0, const substitute_internal::Arg& a1, const substitute_internal::Arg& a2, @@ -451,13 +461,11 @@ "this format string is missing its $0-$4, contains one of " "$5-$9, or contains an unescaped $ character (use $$ instead)"); -void SubstituteAndAppend(std::string* output, const char* format, - const substitute_internal::Arg& a0, - const substitute_internal::Arg& a1, - const substitute_internal::Arg& a2, - const substitute_internal::Arg& a3, - const substitute_internal::Arg& a4, - const substitute_internal::Arg& a5) +void SubstituteAndAppend( + absl::Nonnull<std::string*> output, absl::Nonnull<const char*> format, + const substitute_internal::Arg& a0, const substitute_internal::Arg& a1, + const substitute_internal::Arg& a2, const substitute_internal::Arg& a3, + const substitute_internal::Arg& a4, const substitute_internal::Arg& a5) ABSL_BAD_CALL_IF( substitute_internal::PlaceholderBitmask(format) != 63, "There were 6 substitution arguments given, but " @@ -465,10 +473,11 @@ "$6-$9, or contains an unescaped $ character (use $$ instead)"); void SubstituteAndAppend( - std::string* output, const char* format, const substitute_internal::Arg& a0, - const substitute_internal::Arg& a1, const substitute_internal::Arg& a2, - const substitute_internal::Arg& a3, const substitute_internal::Arg& a4, - const substitute_internal::Arg& a5, const substitute_internal::Arg& a6) + absl::Nonnull<std::string*> output, absl::Nonnull<const char*> format, + const substitute_internal::Arg& a0, const substitute_internal::Arg& a1, + const substitute_internal::Arg& a2, const substitute_internal::Arg& a3, + const substitute_internal::Arg& a4, const substitute_internal::Arg& a5, + const substitute_internal::Arg& a6) ABSL_BAD_CALL_IF( substitute_internal::PlaceholderBitmask(format) != 127, "There were 7 substitution arguments given, but " @@ -476,11 +485,11 @@ "$7-$9, or contains an unescaped $ character (use $$ instead)"); void SubstituteAndAppend( - std::string* output, const char* format, const substitute_internal::Arg& a0, - const substitute_internal::Arg& a1, const substitute_internal::Arg& a2, - const substitute_internal::Arg& a3, const substitute_internal::Arg& a4, - const substitute_internal::Arg& a5, const substitute_internal::Arg& a6, - const substitute_internal::Arg& a7) + absl::Nonnull<std::string*> output, absl::Nonnull<const char*> format, + const substitute_internal::Arg& a0, const substitute_internal::Arg& a1, + const substitute_internal::Arg& a2, const substitute_internal::Arg& a3, + const substitute_internal::Arg& a4, const substitute_internal::Arg& a5, + const substitute_internal::Arg& a6, const substitute_internal::Arg& a7) ABSL_BAD_CALL_IF( substitute_internal::PlaceholderBitmask(format) != 255, "There were 8 substitution arguments given, but " @@ -488,11 +497,12 @@ "$8-$9, or contains an unescaped $ character (use $$ instead)"); void SubstituteAndAppend( - std::string* output, const char* format, const substitute_internal::Arg& a0, - const substitute_internal::Arg& a1, const substitute_internal::Arg& a2, - const substitute_internal::Arg& a3, const substitute_internal::Arg& a4, - const substitute_internal::Arg& a5, const substitute_internal::Arg& a6, - const substitute_internal::Arg& a7, const substitute_internal::Arg& a8) + absl::Nonnull<std::string*> output, absl::Nonnull<const char*> format, + const substitute_internal::Arg& a0, const substitute_internal::Arg& a1, + const substitute_internal::Arg& a2, const substitute_internal::Arg& a3, + const substitute_internal::Arg& a4, const substitute_internal::Arg& a5, + const substitute_internal::Arg& a6, const substitute_internal::Arg& a7, + const substitute_internal::Arg& a8) ABSL_BAD_CALL_IF( substitute_internal::PlaceholderBitmask(format) != 511, "There were 9 substitution arguments given, but " @@ -500,12 +510,12 @@ "contains an unescaped $ character (use $$ instead)"); void SubstituteAndAppend( - std::string* output, const char* format, const substitute_internal::Arg& a0, - const substitute_internal::Arg& a1, const substitute_internal::Arg& a2, - const substitute_internal::Arg& a3, const substitute_internal::Arg& a4, - const substitute_internal::Arg& a5, const substitute_internal::Arg& a6, - const substitute_internal::Arg& a7, const substitute_internal::Arg& a8, - const substitute_internal::Arg& a9) + absl::Nonnull<std::string*> output, absl::Nonnull<const char*> format, + const substitute_internal::Arg& a0, const substitute_internal::Arg& a1, + const substitute_internal::Arg& a2, const substitute_internal::Arg& a3, + const substitute_internal::Arg& a4, const substitute_internal::Arg& a5, + const substitute_internal::Arg& a6, const substitute_internal::Arg& a7, + const substitute_internal::Arg& a8, const substitute_internal::Arg& a9) ABSL_BAD_CALL_IF( substitute_internal::PlaceholderBitmask(format) != 1023, "There were 10 substitution arguments given, but this " @@ -633,20 +643,22 @@ #if defined(ABSL_BAD_CALL_IF) // This body of functions catches cases where the number of placeholders // doesn't match the number of data arguments. -std::string Substitute(const char* format) +std::string Substitute(absl::Nonnull<const char*> format) ABSL_BAD_CALL_IF(substitute_internal::PlaceholderBitmask(format) != 0, "There were no substitution arguments " "but this format string either has a $[0-9] in it or " "contains an unescaped $ character (use $$ instead)"); -std::string Substitute(const char* format, const substitute_internal::Arg& a0) +std::string Substitute(absl::Nonnull<const char*> format, + const substitute_internal::Arg& a0) ABSL_BAD_CALL_IF( substitute_internal::PlaceholderBitmask(format) != 1, "There was 1 substitution argument given, but " "this format string is missing its $0, contains one of $1-$9, " "or contains an unescaped $ character (use $$ instead)"); -std::string Substitute(const char* format, const substitute_internal::Arg& a0, +std::string Substitute(absl::Nonnull<const char*> format, + const substitute_internal::Arg& a0, const substitute_internal::Arg& a1) ABSL_BAD_CALL_IF( substitute_internal::PlaceholderBitmask(format) != 3, @@ -654,7 +666,8 @@ "this format string is missing its $0/$1, contains one of " "$2-$9, or contains an unescaped $ character (use $$ instead)"); -std::string Substitute(const char* format, const substitute_internal::Arg& a0, +std::string Substitute(absl::Nonnull<const char*> format, + const substitute_internal::Arg& a0, const substitute_internal::Arg& a1, const substitute_internal::Arg& a2) ABSL_BAD_CALL_IF( @@ -663,7 +676,8 @@ "this format string is missing its $0/$1/$2, contains one of " "$3-$9, or contains an unescaped $ character (use $$ instead)"); -std::string Substitute(const char* format, const substitute_internal::Arg& a0, +std::string Substitute(absl::Nonnull<const char*> format, + const substitute_internal::Arg& a0, const substitute_internal::Arg& a1, const substitute_internal::Arg& a2, const substitute_internal::Arg& a3) @@ -673,7 +687,8 @@ "this format string is missing its $0-$3, contains one of " "$4-$9, or contains an unescaped $ character (use $$ instead)"); -std::string Substitute(const char* format, const substitute_internal::Arg& a0, +std::string Substitute(absl::Nonnull<const char*> format, + const substitute_internal::Arg& a0, const substitute_internal::Arg& a1, const substitute_internal::Arg& a2, const substitute_internal::Arg& a3, @@ -684,7 +699,8 @@ "this format string is missing its $0-$4, contains one of " "$5-$9, or contains an unescaped $ character (use $$ instead)"); -std::string Substitute(const char* format, const substitute_internal::Arg& a0, +std::string Substitute(absl::Nonnull<const char*> format, + const substitute_internal::Arg& a0, const substitute_internal::Arg& a1, const substitute_internal::Arg& a2, const substitute_internal::Arg& a3, @@ -696,27 +712,23 @@ "this format string is missing its $0-$5, contains one of " "$6-$9, or contains an unescaped $ character (use $$ instead)"); -std::string Substitute(const char* format, const substitute_internal::Arg& a0, - const substitute_internal::Arg& a1, - const substitute_internal::Arg& a2, - const substitute_internal::Arg& a3, - const substitute_internal::Arg& a4, - const substitute_internal::Arg& a5, - const substitute_internal::Arg& a6) +std::string Substitute( + absl::Nonnull<const char*> format, const substitute_internal::Arg& a0, + const substitute_internal::Arg& a1, const substitute_internal::Arg& a2, + const substitute_internal::Arg& a3, const substitute_internal::Arg& a4, + const substitute_internal::Arg& a5, const substitute_internal::Arg& a6) ABSL_BAD_CALL_IF( substitute_internal::PlaceholderBitmask(format) != 127, "There were 7 substitution arguments given, but " "this format string is missing its $0-$6, contains one of " "$7-$9, or contains an unescaped $ character (use $$ instead)"); -std::string Substitute(const char* format, const substitute_internal::Arg& a0, - const substitute_internal::Arg& a1, - const substitute_internal::Arg& a2, - const substitute_internal::Arg& a3, - const substitute_internal::Arg& a4, - const substitute_internal::Arg& a5, - const substitute_internal::Arg& a6, - const substitute_internal::Arg& a7) +std::string Substitute( + absl::Nonnull<const char*> format, const substitute_internal::Arg& a0, + const substitute_internal::Arg& a1, const substitute_internal::Arg& a2, + const substitute_internal::Arg& a3, const substitute_internal::Arg& a4, + const substitute_internal::Arg& a5, const substitute_internal::Arg& a6, + const substitute_internal::Arg& a7) ABSL_BAD_CALL_IF( substitute_internal::PlaceholderBitmask(format) != 255, "There were 8 substitution arguments given, but " @@ -724,7 +736,7 @@ "$8-$9, or contains an unescaped $ character (use $$ instead)"); std::string Substitute( - const char* format, const substitute_internal::Arg& a0, + absl::Nonnull<const char*> format, const substitute_internal::Arg& a0, const substitute_internal::Arg& a1, const substitute_internal::Arg& a2, const substitute_internal::Arg& a3, const substitute_internal::Arg& a4, const substitute_internal::Arg& a5, const substitute_internal::Arg& a6, @@ -736,7 +748,7 @@ "contains an unescaped $ character (use $$ instead)"); std::string Substitute( - const char* format, const substitute_internal::Arg& a0, + absl::Nonnull<const char*> format, const substitute_internal::Arg& a0, const substitute_internal::Arg& a1, const substitute_internal::Arg& a2, const substitute_internal::Arg& a3, const substitute_internal::Arg& a4, const substitute_internal::Arg& a5, const substitute_internal::Arg& a6,
diff --git a/absl/types/BUILD.bazel b/absl/types/BUILD.bazel index 3d5cf01..ce8f605 100644 --- a/absl/types/BUILD.bazel +++ b/absl/types/BUILD.bazel
@@ -118,6 +118,7 @@ deps = [ "//absl/algorithm", "//absl/base:core_headers", + "//absl/base:nullability", "//absl/base:throw_delegate", "//absl/meta:type_traits", ], @@ -154,6 +155,7 @@ "//absl/base:base_internal", "//absl/base:config", "//absl/base:core_headers", + "//absl/base:nullability", "//absl/memory", "//absl/meta:type_traits", "//absl/utility", @@ -293,6 +295,7 @@ copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ + "//absl/base:config", "//absl/base:core_headers", "//absl/meta:type_traits", ],
diff --git a/absl/types/CMakeLists.txt b/absl/types/CMakeLists.txt index 1adf3c7..92b4ae4 100644 --- a/absl/types/CMakeLists.txt +++ b/absl/types/CMakeLists.txt
@@ -115,6 +115,7 @@ DEPS absl::algorithm absl::core_headers + absl::nullability absl::throw_delegate absl::type_traits PUBLIC @@ -175,6 +176,7 @@ absl::config absl::core_headers absl::memory + absl::nullability absl::type_traits absl::utility PUBLIC @@ -284,6 +286,7 @@ COPTS ${ABSL_DEFAULT_COPTS} DEPS + absl::config absl::core_headers absl::type_traits PUBLIC
diff --git a/absl/types/compare.h b/absl/types/compare.h index 2b89b69..3cf4a91 100644 --- a/absl/types/compare.h +++ b/absl/types/compare.h
@@ -16,20 +16,31 @@ // compare.h // ----------------------------------------------------------------------------- // -// This header file defines the `absl::weak_equality`, `absl::strong_equality`, -// `absl::partial_ordering`, `absl::weak_ordering`, and `absl::strong_ordering` -// types for storing the results of three way comparisons. +// This header file defines the `absl::partial_ordering`, `absl::weak_ordering`, +// and `absl::strong_ordering` types for storing the results of three way +// comparisons. // // Example: // absl::weak_ordering compare(const std::string& a, const std::string& b); // // These are C++11 compatible versions of the C++20 corresponding types -// (`std::weak_equality`, etc.) and are designed to be drop-in replacements +// (`std::partial_ordering`, etc.) and are designed to be drop-in replacements // for code compliant with C++20. #ifndef ABSL_TYPES_COMPARE_H_ #define ABSL_TYPES_COMPARE_H_ +#include "absl/base/config.h" + +#ifdef ABSL_USES_STD_ORDERING + +#include <compare> // IWYU pragma: export +#include <type_traits> + +#include "absl/meta/type_traits.h" + +#else + #include <cstddef> #include <cstdint> #include <cstdlib> @@ -39,8 +50,19 @@ #include "absl/base/macros.h" #include "absl/meta/type_traits.h" +#endif + namespace absl { ABSL_NAMESPACE_BEGIN + +#ifdef ABSL_USES_STD_ORDERING + +using std::partial_ordering; +using std::strong_ordering; +using std::weak_ordering; + +#else + namespace compare_internal { using value_type = int8_t; @@ -122,20 +144,6 @@ // in the header file (for performance) without using inline variables (which // aren't available in C++11). template <typename T> -struct weak_equality_base { - ABSL_COMPARE_INLINE_BASECLASS_DECL(equivalent); - ABSL_COMPARE_INLINE_BASECLASS_DECL(nonequivalent); -}; - -template <typename T> -struct strong_equality_base { - ABSL_COMPARE_INLINE_BASECLASS_DECL(equal); - ABSL_COMPARE_INLINE_BASECLASS_DECL(nonequal); - ABSL_COMPARE_INLINE_BASECLASS_DECL(equivalent); - ABSL_COMPARE_INLINE_BASECLASS_DECL(nonequivalent); -}; - -template <typename T> struct partial_ordering_base { ABSL_COMPARE_INLINE_BASECLASS_DECL(less); ABSL_COMPARE_INLINE_BASECLASS_DECL(equivalent); @@ -160,104 +168,6 @@ } // namespace compare_internal -class weak_equality - : public compare_internal::weak_equality_base<weak_equality> { - explicit constexpr weak_equality(compare_internal::eq v) noexcept - : value_(static_cast<compare_internal::value_type>(v)) {} - friend struct compare_internal::weak_equality_base<weak_equality>; - - public: - ABSL_COMPARE_INLINE_SUBCLASS_DECL(weak_equality, equivalent); - ABSL_COMPARE_INLINE_SUBCLASS_DECL(weak_equality, nonequivalent); - - // Comparisons - friend constexpr bool operator==( - weak_equality v, compare_internal::OnlyLiteralZero) noexcept { - return v.value_ == 0; - } - friend constexpr bool operator!=( - weak_equality v, compare_internal::OnlyLiteralZero) noexcept { - return v.value_ != 0; - } - friend constexpr bool operator==(compare_internal::OnlyLiteralZero, - weak_equality v) noexcept { - return 0 == v.value_; - } - friend constexpr bool operator!=(compare_internal::OnlyLiteralZero, - weak_equality v) noexcept { - return 0 != v.value_; - } - friend constexpr bool operator==(weak_equality v1, - weak_equality v2) noexcept { - return v1.value_ == v2.value_; - } - friend constexpr bool operator!=(weak_equality v1, - weak_equality v2) noexcept { - return v1.value_ != v2.value_; - } - - private: - compare_internal::value_type value_; -}; -ABSL_COMPARE_INLINE_INIT(weak_equality, equivalent, - compare_internal::eq::equivalent); -ABSL_COMPARE_INLINE_INIT(weak_equality, nonequivalent, - compare_internal::eq::nonequivalent); - -class strong_equality - : public compare_internal::strong_equality_base<strong_equality> { - explicit constexpr strong_equality(compare_internal::eq v) noexcept - : value_(static_cast<compare_internal::value_type>(v)) {} - friend struct compare_internal::strong_equality_base<strong_equality>; - - public: - ABSL_COMPARE_INLINE_SUBCLASS_DECL(strong_equality, equal); - ABSL_COMPARE_INLINE_SUBCLASS_DECL(strong_equality, nonequal); - ABSL_COMPARE_INLINE_SUBCLASS_DECL(strong_equality, equivalent); - ABSL_COMPARE_INLINE_SUBCLASS_DECL(strong_equality, nonequivalent); - - // Conversion - constexpr operator weak_equality() const noexcept { // NOLINT - return value_ == 0 ? weak_equality::equivalent - : weak_equality::nonequivalent; - } - // Comparisons - friend constexpr bool operator==( - strong_equality v, compare_internal::OnlyLiteralZero) noexcept { - return v.value_ == 0; - } - friend constexpr bool operator!=( - strong_equality v, compare_internal::OnlyLiteralZero) noexcept { - return v.value_ != 0; - } - friend constexpr bool operator==(compare_internal::OnlyLiteralZero, - strong_equality v) noexcept { - return 0 == v.value_; - } - friend constexpr bool operator!=(compare_internal::OnlyLiteralZero, - strong_equality v) noexcept { - return 0 != v.value_; - } - friend constexpr bool operator==(strong_equality v1, - strong_equality v2) noexcept { - return v1.value_ == v2.value_; - } - friend constexpr bool operator!=(strong_equality v1, - strong_equality v2) noexcept { - return v1.value_ != v2.value_; - } - - private: - compare_internal::value_type value_; -}; -ABSL_COMPARE_INLINE_INIT(strong_equality, equal, compare_internal::eq::equal); -ABSL_COMPARE_INLINE_INIT(strong_equality, nonequal, - compare_internal::eq::nonequal); -ABSL_COMPARE_INLINE_INIT(strong_equality, equivalent, - compare_internal::eq::equivalent); -ABSL_COMPARE_INLINE_INIT(strong_equality, nonequivalent, - compare_internal::eq::nonequivalent); - class partial_ordering : public compare_internal::partial_ordering_base<partial_ordering> { explicit constexpr partial_ordering(compare_internal::eq v) noexcept @@ -279,11 +189,6 @@ ABSL_COMPARE_INLINE_SUBCLASS_DECL(partial_ordering, greater); ABSL_COMPARE_INLINE_SUBCLASS_DECL(partial_ordering, unordered); - // Conversion - constexpr operator weak_equality() const noexcept { // NOLINT - return value_ == 0 ? weak_equality::equivalent - : weak_equality::nonequivalent; - } // Comparisons friend constexpr bool operator==( partial_ordering v, compare_internal::OnlyLiteralZero) noexcept { @@ -367,10 +272,6 @@ ABSL_COMPARE_INLINE_SUBCLASS_DECL(weak_ordering, greater); // Conversions - constexpr operator weak_equality() const noexcept { // NOLINT - return value_ == 0 ? weak_equality::equivalent - : weak_equality::nonequivalent; - } constexpr operator partial_ordering() const noexcept { // NOLINT return value_ == 0 ? partial_ordering::equivalent : (value_ < 0 ? partial_ordering::less @@ -458,13 +359,6 @@ ABSL_COMPARE_INLINE_SUBCLASS_DECL(strong_ordering, greater); // Conversions - constexpr operator weak_equality() const noexcept { // NOLINT - return value_ == 0 ? weak_equality::equivalent - : weak_equality::nonequivalent; - } - constexpr operator strong_equality() const noexcept { // NOLINT - return value_ == 0 ? strong_equality::equal : strong_equality::nonequal; - } constexpr operator partial_ordering() const noexcept { // NOLINT return value_ == 0 ? partial_ordering::equivalent : (value_ < 0 ? partial_ordering::less @@ -547,6 +441,8 @@ #undef ABSL_COMPARE_INLINE_SUBCLASS_DECL #undef ABSL_COMPARE_INLINE_INIT +#endif // ABSL_USES_STD_ORDERING + namespace compare_internal { // We also provide these comparator adapter functions for internal absl use.
diff --git a/absl/types/compare_test.cc b/absl/types/compare_test.cc index 8095baf..455cdbb 100644 --- a/absl/types/compare_test.cc +++ b/absl/types/compare_test.cc
@@ -26,45 +26,6 @@ // to an int, which can't be converted to the unspecified zero type. bool Identity(bool b) { return b; } -TEST(Compare, WeakEquality) { - EXPECT_TRUE(Identity(weak_equality::equivalent == 0)); - EXPECT_TRUE(Identity(0 == weak_equality::equivalent)); - EXPECT_TRUE(Identity(weak_equality::nonequivalent != 0)); - EXPECT_TRUE(Identity(0 != weak_equality::nonequivalent)); - const weak_equality values[] = {weak_equality::equivalent, - weak_equality::nonequivalent}; - for (const auto& lhs : values) { - for (const auto& rhs : values) { - const bool are_equal = &lhs == &rhs; - EXPECT_EQ(lhs == rhs, are_equal); - EXPECT_EQ(lhs != rhs, !are_equal); - } - } -} - -TEST(Compare, StrongEquality) { - EXPECT_TRUE(Identity(strong_equality::equal == 0)); - EXPECT_TRUE(Identity(0 == strong_equality::equal)); - EXPECT_TRUE(Identity(strong_equality::nonequal != 0)); - EXPECT_TRUE(Identity(0 != strong_equality::nonequal)); - EXPECT_TRUE(Identity(strong_equality::equivalent == 0)); - EXPECT_TRUE(Identity(0 == strong_equality::equivalent)); - EXPECT_TRUE(Identity(strong_equality::nonequivalent != 0)); - EXPECT_TRUE(Identity(0 != strong_equality::nonequivalent)); - const strong_equality values[] = {strong_equality::equal, - strong_equality::nonequal}; - for (const auto& lhs : values) { - for (const auto& rhs : values) { - const bool are_equal = &lhs == &rhs; - EXPECT_EQ(lhs == rhs, are_equal); - EXPECT_EQ(lhs != rhs, !are_equal); - } - } - EXPECT_TRUE(Identity(strong_equality::equivalent == strong_equality::equal)); - EXPECT_TRUE( - Identity(strong_equality::nonequivalent == strong_equality::nonequal)); -} - TEST(Compare, PartialOrdering) { EXPECT_TRUE(Identity(partial_ordering::less < 0)); EXPECT_TRUE(Identity(0 > partial_ordering::less)); @@ -147,30 +108,6 @@ TEST(Compare, Conversions) { EXPECT_TRUE( - Identity(implicit_cast<weak_equality>(strong_equality::equal) == 0)); - EXPECT_TRUE( - Identity(implicit_cast<weak_equality>(strong_equality::nonequal) != 0)); - EXPECT_TRUE( - Identity(implicit_cast<weak_equality>(strong_equality::equivalent) == 0)); - EXPECT_TRUE(Identity( - implicit_cast<weak_equality>(strong_equality::nonequivalent) != 0)); - - EXPECT_TRUE( - Identity(implicit_cast<weak_equality>(partial_ordering::less) != 0)); - EXPECT_TRUE(Identity( - implicit_cast<weak_equality>(partial_ordering::equivalent) == 0)); - EXPECT_TRUE( - Identity(implicit_cast<weak_equality>(partial_ordering::greater) != 0)); - EXPECT_TRUE( - Identity(implicit_cast<weak_equality>(partial_ordering::unordered) != 0)); - - EXPECT_TRUE(implicit_cast<weak_equality>(weak_ordering::less) != 0); - EXPECT_TRUE( - Identity(implicit_cast<weak_equality>(weak_ordering::equivalent) == 0)); - EXPECT_TRUE( - Identity(implicit_cast<weak_equality>(weak_ordering::greater) != 0)); - - EXPECT_TRUE( Identity(implicit_cast<partial_ordering>(weak_ordering::less) != 0)); EXPECT_TRUE( Identity(implicit_cast<partial_ordering>(weak_ordering::less) < 0)); @@ -186,24 +123,6 @@ Identity(implicit_cast<partial_ordering>(weak_ordering::greater) >= 0)); EXPECT_TRUE( - Identity(implicit_cast<weak_equality>(strong_ordering::less) != 0)); - EXPECT_TRUE( - Identity(implicit_cast<weak_equality>(strong_ordering::equal) == 0)); - EXPECT_TRUE( - Identity(implicit_cast<weak_equality>(strong_ordering::equivalent) == 0)); - EXPECT_TRUE( - Identity(implicit_cast<weak_equality>(strong_ordering::greater) != 0)); - - EXPECT_TRUE( - Identity(implicit_cast<strong_equality>(strong_ordering::less) != 0)); - EXPECT_TRUE( - Identity(implicit_cast<strong_equality>(strong_ordering::equal) == 0)); - EXPECT_TRUE(Identity( - implicit_cast<strong_equality>(strong_ordering::equivalent) == 0)); - EXPECT_TRUE( - Identity(implicit_cast<strong_equality>(strong_ordering::greater) != 0)); - - EXPECT_TRUE( Identity(implicit_cast<partial_ordering>(strong_ordering::less) != 0)); EXPECT_TRUE( Identity(implicit_cast<partial_ordering>(strong_ordering::less) < 0)); @@ -360,14 +279,6 @@ #ifdef __cpp_inline_variables TEST(Compare, StaticAsserts) { - static_assert(weak_equality::equivalent == 0, ""); - static_assert(weak_equality::nonequivalent != 0, ""); - - static_assert(strong_equality::equal == 0, ""); - static_assert(strong_equality::nonequal != 0, ""); - static_assert(strong_equality::equivalent == 0, ""); - static_assert(strong_equality::nonequivalent != 0, ""); - static_assert(partial_ordering::less < 0, ""); static_assert(partial_ordering::equivalent == 0, ""); static_assert(partial_ordering::greater > 0, "");
diff --git a/absl/types/optional.h b/absl/types/optional.h index 0a8080d..395fe62 100644 --- a/absl/types/optional.h +++ b/absl/types/optional.h
@@ -61,6 +61,7 @@ #include <utility> #include "absl/base/attributes.h" +#include "absl/base/nullability.h" #include "absl/base/internal/inline_variable.h" #include "absl/meta/type_traits.h" #include "absl/types/bad_optional_access.h" @@ -415,11 +416,11 @@ // `optional` is empty, behavior is undefined. // // If you need myOpt->foo in constexpr, use (*myOpt).foo instead. - const T* operator->() const ABSL_ATTRIBUTE_LIFETIME_BOUND { + absl::Nonnull<const T*> operator->() const ABSL_ATTRIBUTE_LIFETIME_BOUND { ABSL_HARDENING_ASSERT(this->engaged_); return std::addressof(this->data_); } - T* operator->() ABSL_ATTRIBUTE_LIFETIME_BOUND { + absl::Nonnull<T*> operator->() ABSL_ATTRIBUTE_LIFETIME_BOUND { ABSL_HARDENING_ASSERT(this->engaged_); return std::addressof(this->data_); }
diff --git a/absl/types/span.h b/absl/types/span.h index 70ed8eb..7d1d016 100644 --- a/absl/types/span.h +++ b/absl/types/span.h
@@ -63,6 +63,7 @@ #include "absl/base/attributes.h" #include "absl/base/internal/throw_delegate.h" #include "absl/base/macros.h" +#include "absl/base/nullability.h" #include "absl/base/optimization.h" #include "absl/base/port.h" // TODO(strel): remove this include #include "absl/meta/type_traits.h" @@ -172,8 +173,8 @@ public: using element_type = T; using value_type = absl::remove_cv_t<T>; - using pointer = T*; - using const_pointer = const T*; + using pointer = absl::Nullable<T*>; + using const_pointer = absl::Nullable<const T*>; using reference = T&; using const_reference = const T&; using iterator = pointer; @@ -679,12 +680,12 @@ // } // template <int&... ExplicitArgumentBarrier, typename T> -constexpr Span<T> MakeSpan(T* ptr, size_t size) noexcept { +constexpr Span<T> MakeSpan(absl::Nullable<T*> ptr, size_t size) noexcept { return Span<T>(ptr, size); } template <int&... ExplicitArgumentBarrier, typename T> -Span<T> MakeSpan(T* begin, T* end) noexcept { +Span<T> MakeSpan(absl::Nullable<T*> begin, absl::Nullable<T*> end) noexcept { return ABSL_HARDENING_ASSERT(begin <= end), Span<T>(begin, static_cast<size_t>(end - begin)); } @@ -725,12 +726,14 @@ // ProcessInts(absl::MakeConstSpan(std::vector<int>{ 0, 0, 0 })); // template <int&... ExplicitArgumentBarrier, typename T> -constexpr Span<const T> MakeConstSpan(T* ptr, size_t size) noexcept { +constexpr Span<const T> MakeConstSpan(absl::Nullable<T*> ptr, + size_t size) noexcept { return Span<const T>(ptr, size); } template <int&... ExplicitArgumentBarrier, typename T> -Span<const T> MakeConstSpan(T* begin, T* end) noexcept { +Span<const T> MakeConstSpan(absl::Nullable<T*> begin, + absl::Nullable<T*> end) noexcept { return ABSL_HARDENING_ASSERT(begin <= end), Span<const T>(begin, end - begin); }
diff --git a/absl/utility/utility.h b/absl/utility/utility.h index bf92322..fc0d1f6 100644 --- a/absl/utility/utility.h +++ b/absl/utility/utility.h
@@ -12,17 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. // -// This header file contains C++11 versions of standard <utility> header -// abstractions available within C++14 and C++17, and are designed to be drop-in +// This header file contains C++14 versions of standard <utility> header +// abstractions available within C++17, and are designed to be drop-in // replacement for code compliant with C++14 and C++17. // // The following abstractions are defined: // -// * integer_sequence<T, Ints...> == std::integer_sequence<T, Ints...> -// * index_sequence<Ints...> == std::index_sequence<Ints...> -// * make_integer_sequence<T, N> == std::make_integer_sequence<T, N> -// * make_index_sequence<N> == std::make_index_sequence<N> -// * index_sequence_for<Ts...> == std::index_sequence_for<Ts...> // * apply<Functor, Tuple> == std::apply<Functor, Tuple> // * exchange<T> == std::exchange<T> // * make_from_tuple<T> == std::make_from_tuple<T> @@ -33,7 +28,6 @@ // // References: // -// https://en.cppreference.com/w/cpp/utility/integer_sequence // https://en.cppreference.com/w/cpp/utility/apply // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3658.html @@ -53,68 +47,18 @@ namespace absl { ABSL_NAMESPACE_BEGIN -// integer_sequence -// -// Class template representing a compile-time integer sequence. An instantiation -// of `integer_sequence<T, Ints...>` has a sequence of integers encoded in its -// type through its template arguments (which is a common need when -// working with C++11 variadic templates). `absl::integer_sequence` is designed -// to be a drop-in replacement for C++14's `std::integer_sequence`. -// -// Example: -// -// template< class T, T... Ints > -// void user_function(integer_sequence<T, Ints...>); -// -// int main() -// { -// // user_function's `T` will be deduced to `int` and `Ints...` -// // will be deduced to `0, 1, 2, 3, 4`. -// user_function(make_integer_sequence<int, 5>()); -// } -template <typename T, T... Ints> -struct integer_sequence { - using value_type = T; - static constexpr size_t size() noexcept { return sizeof...(Ints); } -}; - -// index_sequence -// -// A helper template for an `integer_sequence` of `size_t`, -// `absl::index_sequence` is designed to be a drop-in replacement for C++14's -// `std::index_sequence`. -template <size_t... Ints> -using index_sequence = integer_sequence<size_t, Ints...>; +// Historical note: Abseil once provided implementations of these +// abstractions for platforms that had not yet provided them. Those +// platforms are no longer supported. New code should simply use the +// the ones from std directly. +using std::index_sequence; +using std::index_sequence_for; +using std::integer_sequence; +using std::make_index_sequence; +using std::make_integer_sequence; namespace utility_internal { -template <typename Seq, size_t SeqSize, size_t Rem> -struct Extend; - -// Note that SeqSize == sizeof...(Ints). It's passed explicitly for efficiency. -template <typename T, T... Ints, size_t SeqSize> -struct Extend<integer_sequence<T, Ints...>, SeqSize, 0> { - using type = integer_sequence<T, Ints..., (Ints + SeqSize)...>; -}; - -template <typename T, T... Ints, size_t SeqSize> -struct Extend<integer_sequence<T, Ints...>, SeqSize, 1> { - using type = integer_sequence<T, Ints..., (Ints + SeqSize)..., 2 * SeqSize>; -}; - -// Recursion helper for 'make_integer_sequence<T, N>'. -// 'Gen<T, N>::type' is an alias for 'integer_sequence<T, 0, 1, ... N-1>'. -template <typename T, size_t N> -struct Gen { - using type = - typename Extend<typename Gen<T, N / 2>::type, N / 2, N % 2>::type; -}; - -template <typename T> -struct Gen<T, 0> { - using type = integer_sequence<T>; -}; - template <typename T> struct InPlaceTypeTag { explicit InPlaceTypeTag() = delete; @@ -131,32 +75,6 @@ } // namespace utility_internal -// Compile-time sequences of integers - -// make_integer_sequence -// -// This template alias is equivalent to -// `integer_sequence<int, 0, 1, ..., N-1>`, and is designed to be a drop-in -// replacement for C++14's `std::make_integer_sequence`. -template <typename T, T N> -using make_integer_sequence = typename utility_internal::Gen<T, N>::type; - -// make_index_sequence -// -// This template alias is equivalent to `index_sequence<0, 1, ..., N-1>`, -// and is designed to be a drop-in replacement for C++14's -// `std::make_index_sequence`. -template <size_t N> -using make_index_sequence = make_integer_sequence<size_t, N>; - -// index_sequence_for -// -// Converts a typename pack into an index sequence of the same length, and -// is designed to be a drop-in replacement for C++14's -// `std::index_sequence_for()` -template <typename... Ts> -using index_sequence_for = make_index_sequence<sizeof...(Ts)>; - // Tag types #ifdef ABSL_USES_STD_OPTIONAL
diff --git a/absl/utility/utility_test.cc b/absl/utility/utility_test.cc index 2f0509a..1af6813 100644 --- a/absl/utility/utility_test.cc +++ b/absl/utility/utility_test.cc
@@ -30,139 +30,10 @@ namespace { -#ifdef _MSC_VER -// Warnings for unused variables in this test are false positives. On other -// platforms, they are suppressed by ABSL_ATTRIBUTE_UNUSED, but that doesn't -// work on MSVC. -// Both the unused variables and the name length warnings are due to calls -// to absl::make_index_sequence with very large values, creating very long type -// names. The resulting warnings are so long they make build output unreadable. -#pragma warning(push) -#pragma warning(disable : 4503) // decorated name length exceeded -#pragma warning(disable : 4101) // unreferenced local variable -#endif // _MSC_VER - using ::testing::ElementsAre; using ::testing::Pointee; using ::testing::StaticAssertTypeEq; -TEST(IntegerSequenceTest, ValueType) { - StaticAssertTypeEq<int, absl::integer_sequence<int>::value_type>(); - StaticAssertTypeEq<char, absl::integer_sequence<char>::value_type>(); -} - -TEST(IntegerSequenceTest, Size) { - EXPECT_EQ(0, (absl::integer_sequence<int>::size())); - EXPECT_EQ(1, (absl::integer_sequence<int, 0>::size())); - EXPECT_EQ(1, (absl::integer_sequence<int, 1>::size())); - EXPECT_EQ(2, (absl::integer_sequence<int, 1, 2>::size())); - EXPECT_EQ(3, (absl::integer_sequence<int, 0, 1, 2>::size())); - EXPECT_EQ(3, (absl::integer_sequence<int, -123, 123, 456>::size())); - constexpr size_t sz = absl::integer_sequence<int, 0, 1>::size(); - EXPECT_EQ(2, sz); -} - -TEST(IntegerSequenceTest, MakeIndexSequence) { - StaticAssertTypeEq<absl::index_sequence<>, absl::make_index_sequence<0>>(); - StaticAssertTypeEq<absl::index_sequence<0>, absl::make_index_sequence<1>>(); - StaticAssertTypeEq<absl::index_sequence<0, 1>, - absl::make_index_sequence<2>>(); - StaticAssertTypeEq<absl::index_sequence<0, 1, 2>, - absl::make_index_sequence<3>>(); -} - -TEST(IntegerSequenceTest, MakeIntegerSequence) { - StaticAssertTypeEq<absl::integer_sequence<int>, - absl::make_integer_sequence<int, 0>>(); - StaticAssertTypeEq<absl::integer_sequence<int, 0>, - absl::make_integer_sequence<int, 1>>(); - StaticAssertTypeEq<absl::integer_sequence<int, 0, 1>, - absl::make_integer_sequence<int, 2>>(); - StaticAssertTypeEq<absl::integer_sequence<int, 0, 1, 2>, - absl::make_integer_sequence<int, 3>>(); -} - -template <typename... Ts> -class Counter {}; - -template <size_t... Is> -void CountAll(absl::index_sequence<Is...>) { - // We only need an alias here, but instantiate a variable to silence warnings - // for unused typedefs in some compilers. - ABSL_ATTRIBUTE_UNUSED Counter<absl::make_index_sequence<Is>...> seq; -} - -// This test verifies that absl::make_index_sequence can handle large arguments -// without blowing up template instantiation stack, going OOM or taking forever -// to compile (there is hard 15 minutes limit imposed by forge). -TEST(IntegerSequenceTest, MakeIndexSequencePerformance) { - // O(log N) template instantiations. - // We only need an alias here, but instantiate a variable to silence warnings - // for unused typedefs in some compilers. - ABSL_ATTRIBUTE_UNUSED absl::make_index_sequence<(1 << 16) - 1> seq; - // O(N) template instantiations. - CountAll(absl::make_index_sequence<(1 << 8) - 1>()); -} - -template <typename F, typename Tup, size_t... Is> -auto ApplyFromTupleImpl(F f, const Tup& tup, absl::index_sequence<Is...>) - -> decltype(f(std::get<Is>(tup)...)) { - return f(std::get<Is>(tup)...); -} - -template <typename Tup> -using TupIdxSeq = absl::make_index_sequence<std::tuple_size<Tup>::value>; - -template <typename F, typename Tup> -auto ApplyFromTuple(F f, const Tup& tup) - -> decltype(ApplyFromTupleImpl(f, tup, TupIdxSeq<Tup>{})) { - return ApplyFromTupleImpl(f, tup, TupIdxSeq<Tup>{}); -} - -template <typename T> -std::string Fmt(const T& x) { - std::ostringstream os; - os << x; - return os.str(); -} - -struct PoorStrCat { - template <typename... Args> - std::string operator()(const Args&... args) const { - std::string r; - for (const auto& e : {Fmt(args)...}) r += e; - return r; - } -}; - -template <typename Tup, size_t... Is> -std::vector<std::string> TupStringVecImpl(const Tup& tup, - absl::index_sequence<Is...>) { - return {Fmt(std::get<Is>(tup))...}; -} - -template <typename... Ts> -std::vector<std::string> TupStringVec(const std::tuple<Ts...>& tup) { - return TupStringVecImpl(tup, absl::index_sequence_for<Ts...>()); -} - -TEST(MakeIndexSequenceTest, ApplyFromTupleExample) { - PoorStrCat f{}; - EXPECT_EQ("12abc3.14", f(12, "abc", 3.14)); - EXPECT_EQ("12abc3.14", ApplyFromTuple(f, std::make_tuple(12, "abc", 3.14))); -} - -TEST(IndexSequenceForTest, Basic) { - StaticAssertTypeEq<absl::index_sequence<>, absl::index_sequence_for<>>(); - StaticAssertTypeEq<absl::index_sequence<0>, absl::index_sequence_for<int>>(); - StaticAssertTypeEq<absl::index_sequence<0, 1, 2, 3>, - absl::index_sequence_for<int, void, char, int>>(); -} - -TEST(IndexSequenceForTest, Example) { - EXPECT_THAT(TupStringVec(std::make_tuple(12, "abc", 3.14)), - ElementsAre("12", "abc", "3.14")); -} int Function(int a, int b) { return a - b; }
diff --git a/ci/absl_alternate_options.h b/ci/absl_alternate_options.h index 82d2ecf..a563859 100644 --- a/ci/absl_alternate_options.h +++ b/ci/absl_alternate_options.h
@@ -22,6 +22,7 @@ #define ABSL_OPTION_USE_STD_OPTIONAL 0 #define ABSL_OPTION_USE_STD_STRING_VIEW 0 #define ABSL_OPTION_USE_STD_VARIANT 0 +#define ABSL_OPTION_USE_STD_ORDERING 0 #define ABSL_OPTION_USE_INLINE_NAMESPACE 1 #define ABSL_OPTION_INLINE_NAMESPACE_NAME ns #define ABSL_OPTION_HARDENED 1