Roll abseil_revision 436ba6c4a0..27c30ec671.
Change Log:
https://chromium.googlesource.com/external/github.com/abseil/abseil-cpp/+log/436ba6c4a0..27c30ec671
Full diff:
https://chromium.googlesource.com/external/github.com/abseil/abseil-cpp/+/436ba6c4a0..27c30ec671
Bug: None
Change-Id: Iafc0aabd56d9f5506774a37427718f616bdff7d9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1626859
Reviewed-by: Nico Weber <thakis@chromium.org>
Commit-Queue: Mirko Bonadei <mbonadei@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#662640}
Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src
Cr-Mirrored-Commit: 8f6f8bc75db2ca8e45cc7f3956b096b170049a33
diff --git a/README.chromium b/README.chromium
index 332c140..2edc12f 100644
--- a/README.chromium
+++ b/README.chromium
@@ -4,7 +4,7 @@
License: Apache 2.0
License File: LICENSE
Version: 0
-Revision: 436ba6c4a0ea3a06eca6e055f9c8d296bf3bae12
+Revision: 27c30ec671cb7b5ba84c4e79feff7fd0b0ac6338
Security Critical: yes
Description:
diff --git a/absl/base/call_once.h b/absl/base/call_once.h
index 8c4f297..e7fc230 100644
--- a/absl/base/call_once.h
+++ b/absl/base/call_once.h
@@ -29,6 +29,7 @@
#include <atomic>
#include <cstdint>
#include <type_traits>
+#include <utility>
#include "absl/base/internal/invoke.h"
#include "absl/base/internal/low_level_scheduling.h"
@@ -36,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/optimization.h"
#include "absl/base/port.h"
namespace absl {
diff --git a/absl/base/casts.h b/absl/base/casts.h
index a381c42..aba0178 100644
--- a/absl/base/casts.h
+++ b/absl/base/casts.h
@@ -27,6 +27,7 @@
#include <cstring>
#include <memory>
#include <type_traits>
+#include <utility>
#include "absl/base/internal/identity.h"
#include "absl/base/macros.h"
diff --git a/absl/base/internal/low_level_alloc.cc b/absl/base/internal/low_level_alloc.cc
index 1356167..cd526b3 100644
--- a/absl/base/internal/low_level_alloc.cc
+++ b/absl/base/internal/low_level_alloc.cc
@@ -294,7 +294,10 @@
arena_->mu.Unlock();
#ifndef ABSL_LOW_LEVEL_ALLOC_ASYNC_SIGNAL_SAFE_MISSING
if (mask_valid_) {
- pthread_sigmask(SIG_SETMASK, &mask_, nullptr);
+ const int err = pthread_sigmask(SIG_SETMASK, &mask_, nullptr);
+ if (err != 0) {
+ ABSL_RAW_LOG(FATAL, "pthread_sigmask failed: %d", err);
+ }
}
#endif
left_ = true;
diff --git a/absl/base/internal/low_level_alloc.h b/absl/base/internal/low_level_alloc.h
index b35673d..c98cdb3 100644
--- a/absl/base/internal/low_level_alloc.h
+++ b/absl/base/internal/low_level_alloc.h
@@ -25,6 +25,7 @@
// IWYU pragma: private, include "base/low_level_alloc.h"
#include <sys/types.h>
+
#include <cstdint>
#include "absl/base/attributes.h"
diff --git a/absl/base/internal/spinlock.h b/absl/base/internal/spinlock.h
index f05ad83..ecda792 100644
--- a/absl/base/internal/spinlock.h
+++ b/absl/base/internal/spinlock.h
@@ -32,6 +32,7 @@
#include <stdint.h>
#include <sys/types.h>
+
#include <atomic>
#include "absl/base/attributes.h"
@@ -57,8 +58,10 @@
//
// static SpinLock lock(base_internal::kLinkerInitialized);
//
- // When intialized using this constructor, we depend on the fact
- // that the linker has already initialized the memory appropriately.
+ // When initialized using this constructor, we depend on the fact
+ // that the linker has already initialized the memory appropriately. The lock
+ // is initialized in non-cooperative mode.
+ //
// A SpinLock constructed like this can be freely used from global
// initializers without worrying about the order in which global
// initializers run.
diff --git a/absl/base/macros.h b/absl/base/macros.h
index ca62079..606a90a 100644
--- a/absl/base/macros.h
+++ b/absl/base/macros.h
@@ -31,6 +31,7 @@
#include <cassert>
#include <cstddef>
+#include "absl/base/optimization.h"
#include "absl/base/port.h"
// ABSL_ARRAYSIZE()
diff --git a/absl/base/optimization.h b/absl/base/optimization.h
index 6974f1f..0dcbef3 100644
--- a/absl/base/optimization.h
+++ b/absl/base/optimization.h
@@ -163,6 +163,12 @@
// Compilers can use the information that a certain branch is not likely to be
// taken (for instance, a CHECK failure) to optimize for the common case in
// the absence of better information (ie. compiling gcc with `-fprofile-arcs`).
+//
+// Recommendation: Modern CPUs dynamically predict branch execution paths,
+// typically with accuracy greater than 97%. As a result, annotating every
+// branch in a codebase is likely counterproductive; however, annotating
+// specific branches that are both hot and consistently mispredicted is likely
+// to yield performance improvements.
#if ABSL_HAVE_BUILTIN(__builtin_expect) || \
(defined(__GNUC__) && !defined(__clang__))
#define ABSL_PREDICT_FALSE(x) (__builtin_expect(x, 0))
diff --git a/absl/container/inlined_vector.h b/absl/container/inlined_vector.h
index 61e0cfb..564c953 100644
--- a/absl/container/inlined_vector.h
+++ b/absl/container/inlined_vector.h
@@ -155,7 +155,7 @@
// Creates a copy of an `other` inlined vector using `other`'s allocator.
InlinedVector(const InlinedVector& other)
- : InlinedVector(other, other.storage_.GetAllocator()) {}
+ : InlinedVector(other, *other.storage_.GetAllocPtr()) {}
// Creates a copy of an `other` inlined vector using a specified allocator.
InlinedVector(const InlinedVector& other, const allocator_type& alloc)
@@ -189,7 +189,7 @@
InlinedVector(InlinedVector&& other) noexcept(
absl::allocator_is_nothrow<allocator_type>::value ||
std::is_nothrow_move_constructible<value_type>::value)
- : storage_(other.storage_.GetAllocator()) {
+ : storage_(*other.storage_.GetAllocPtr()) {
if (other.storage_.GetIsAllocated()) {
// We can just steal the underlying buffer from the source.
// That leaves the source empty, so we clear its size.
@@ -224,7 +224,7 @@
absl::allocator_is_nothrow<allocator_type>::value)
: storage_(alloc) {
if (other.storage_.GetIsAllocated()) {
- if (alloc == other.storage_.GetAllocator()) {
+ if (*storage_.GetAllocPtr() == *other.storage_.GetAllocPtr()) {
// We can just steal the allocation from the source.
storage_.SetAllocatedSize(other.size());
storage_.SetAllocatedData(other.storage_.GetAllocatedData());
@@ -437,7 +437,7 @@
// `InlinedVector::get_allocator()`
//
// Returns a copy of the allocator of the inlined vector.
- allocator_type get_allocator() const { return storage_.GetAllocator(); }
+ allocator_type get_allocator() const { return *storage_.GetAllocPtr(); }
// ---------------------------------------------------------------------------
// InlinedVector Member Mutators
@@ -448,24 +448,16 @@
// Replaces the contents of the inlined vector with copies of the elements in
// the provided `std::initializer_list`.
InlinedVector& operator=(std::initializer_list<value_type> list) {
- AssignForwardRange(list.begin(), list.end());
+ assign(list.begin(), list.end());
return *this;
}
// Overload of `InlinedVector::operator=()` to replace the contents of the
// inlined vector with the contents of `other`.
InlinedVector& operator=(const InlinedVector& other) {
- if (ABSL_PREDICT_FALSE(this == std::addressof(other))) return *this;
-
- // Optimized to avoid reallocation.
- // Prefer reassignment to copy construction for elements.
- if (size() < other.size()) { // grow
- reserve(other.size());
- std::copy(other.begin(), other.begin() + size(), begin());
- std::copy(other.begin() + size(), other.end(), std::back_inserter(*this));
- } else { // maybe shrink
- erase(begin() + other.size(), end());
- std::copy(other.begin(), other.end(), begin());
+ if (ABSL_PREDICT_TRUE(this != std::addressof(other))) {
+ const_pointer other_data = other.data();
+ assign(other_data, other_data + other.size());
}
return *this;
}
@@ -528,7 +520,7 @@
// inlined vector with copies of the values in the provided
// `std::initializer_list`.
void assign(std::initializer_list<value_type> list) {
- AssignForwardRange(list.begin(), list.end());
+ assign(list.begin(), list.end());
}
// Overload of `InlinedVector::assign()` to replace the contents of the
@@ -536,7 +528,24 @@
template <typename ForwardIterator,
EnableIfAtLeastForwardIterator<ForwardIterator>* = nullptr>
void assign(ForwardIterator first, ForwardIterator last) {
- AssignForwardRange(first, last);
+ auto length = std::distance(first, last);
+
+ // Prefer reassignment to copy construction for elements.
+ if (static_cast<size_type>(length) <= size()) {
+ erase(std::copy(first, last, begin()), end());
+ return;
+ }
+
+ reserve(length);
+ iterator out = begin();
+ for (; out != end(); ++first, ++out) *out = *first;
+ if (storage_.GetIsAllocated()) {
+ UninitializedCopy(first, last, out);
+ storage_.SetAllocatedSize(length);
+ } else {
+ UninitializedCopy(first, last, out);
+ storage_.SetInlinedSize(length);
+ }
}
// Overload of `InlinedVector::assign()` to replace the contents of the
@@ -785,19 +794,15 @@
// deallocates the heap allocation if the inlined vector was allocated.
void clear() noexcept {
const bool is_allocated = storage_.GetIsAllocated();
-
pointer the_data =
is_allocated ? storage_.GetAllocatedData() : storage_.GetInlinedData();
-
- inlined_vector_internal::DestroyElements(storage_.GetAllocator(), the_data,
+ inlined_vector_internal::DestroyElements(storage_.GetAllocPtr(), the_data,
storage_.GetSize());
-
+ storage_.SetInlinedSize(0);
if (is_allocated) {
- AllocatorTraits::deallocate(storage_.GetAllocator(), the_data,
+ AllocatorTraits::deallocate(*storage_.GetAllocPtr(), the_data,
storage_.GetAllocatedCapacity());
}
-
- storage_.SetInlinedSize(/* size = */ 0);
}
// `InlinedVector::reserve()`
@@ -845,7 +850,7 @@
// Reallocate storage and move elements.
// We can't simply use the same approach as above, because `assign()` would
// call into `reserve()` internally and reserve larger capacity than we need
- pointer new_data = AllocatorTraits::allocate(storage_.GetAllocator(), s);
+ pointer new_data = AllocatorTraits::allocate(*storage_.GetAllocPtr(), s);
UninitializedCopy(std::make_move_iterator(storage_.GetAllocatedData()),
std::make_move_iterator(storage_.GetAllocatedData() + s),
new_data);
@@ -871,7 +876,7 @@
Destroy(storage_.GetAllocatedData(),
storage_.GetAllocatedData() + size());
assert(begin() == storage_.GetAllocatedData());
- AllocatorTraits::deallocate(storage_.GetAllocator(),
+ AllocatorTraits::deallocate(*storage_.GetAllocPtr(),
storage_.GetAllocatedData(),
storage_.GetAllocatedCapacity());
} else {
@@ -886,7 +891,7 @@
template <typename... Args>
reference Construct(pointer p, Args&&... args) {
std::allocator_traits<allocator_type>::construct(
- storage_.GetAllocator(), p, std::forward<Args>(args)...);
+ *storage_.GetAllocPtr(), p, std::forward<Args>(args)...);
return *p;
}
@@ -903,7 +908,7 @@
// Destroy [`from`, `to`) in place.
void Destroy(pointer from, pointer to) {
for (pointer cur = from; cur != to; ++cur) {
- std::allocator_traits<allocator_type>::destroy(storage_.GetAllocator(),
+ std::allocator_traits<allocator_type>::destroy(*storage_.GetAllocPtr(),
cur);
}
#if !defined(NDEBUG)
@@ -934,7 +939,7 @@
}
pointer new_data =
- AllocatorTraits::allocate(storage_.GetAllocator(), new_capacity);
+ AllocatorTraits::allocate(*storage_.GetAllocPtr(), new_capacity);
UninitializedCopy(std::make_move_iterator(data()),
std::make_move_iterator(data() + s), new_data);
@@ -966,7 +971,7 @@
// Move everyone into the new allocation, leaving a gap of `n` for the
// requested shift.
pointer new_data =
- AllocatorTraits::allocate(storage_.GetAllocator(), new_capacity);
+ AllocatorTraits::allocate(*storage_.GetAllocPtr(), new_capacity);
size_type index = position - begin();
UninitializedCopy(std::make_move_iterator(data()),
std::make_move_iterator(data() + index), new_data);
@@ -1015,7 +1020,7 @@
size_type new_capacity = 2 * capacity();
pointer new_data =
- AllocatorTraits::allocate(storage_.GetAllocator(), new_capacity);
+ AllocatorTraits::allocate(*storage_.GetAllocPtr(), new_capacity);
reference new_element =
Construct(new_data + s, std::forward<Args>(args)...);
@@ -1029,7 +1034,7 @@
void InitAssign(size_type n) {
if (n > static_cast<size_type>(N)) {
- pointer new_data = AllocatorTraits::allocate(storage_.GetAllocator(), n);
+ pointer new_data = AllocatorTraits::allocate(*storage_.GetAllocPtr(), n);
storage_.SetAllocatedData(new_data);
storage_.SetAllocatedCapacity(n);
UninitializedFill(storage_.GetAllocatedData(),
@@ -1044,7 +1049,7 @@
void InitAssign(size_type n, const_reference v) {
if (n > static_cast<size_type>(N)) {
- pointer new_data = AllocatorTraits::allocate(storage_.GetAllocator(), n);
+ pointer new_data = AllocatorTraits::allocate(*storage_.GetAllocPtr(), n);
storage_.SetAllocatedData(new_data);
storage_.SetAllocatedCapacity(n);
UninitializedFill(storage_.GetAllocatedData(),
@@ -1058,32 +1063,6 @@
}
template <typename ForwardIt>
- void AssignForwardRange(ForwardIt first, ForwardIt last) {
- static_assert(absl::inlined_vector_internal::IsAtLeastForwardIterator<
- ForwardIt>::value,
- "");
-
- auto length = std::distance(first, last);
-
- // Prefer reassignment to copy construction for elements.
- if (static_cast<size_type>(length) <= size()) {
- erase(std::copy(first, last, begin()), end());
- return;
- }
-
- reserve(length);
- iterator out = begin();
- for (; out != end(); ++first, ++out) *out = *first;
- if (storage_.GetIsAllocated()) {
- UninitializedCopy(first, last, out);
- storage_.SetAllocatedSize(length);
- } else {
- UninitializedCopy(first, last, out);
- storage_.SetInlinedSize(length);
- }
- }
-
- template <typename ForwardIt>
void AppendForwardRange(ForwardIt first, ForwardIt last) {
static_assert(absl::inlined_vector_internal::IsAtLeastForwardIterator<
ForwardIt>::value,
@@ -1143,7 +1122,7 @@
// Both out of line, so just swap the tag, allocation, and allocator.
storage_.SwapSizeAndIsAllocated(std::addressof(other.storage_));
storage_.SwapAllocatedSizeAndCapacity(std::addressof(other.storage_));
- swap(storage_.GetAllocator(), other.storage_.GetAllocator());
+ swap(*storage_.GetAllocPtr(), *other.storage_.GetAllocPtr());
return;
}
@@ -1173,7 +1152,7 @@
a->storage_.GetInlinedData() + a_size);
storage_.SwapSizeAndIsAllocated(std::addressof(other.storage_));
- swap(storage_.GetAllocator(), other.storage_.GetAllocator());
+ swap(*storage_.GetAllocPtr(), *other.storage_.GetAllocPtr());
assert(b->size() == a_size);
assert(a->size() == b_size);
@@ -1215,8 +1194,8 @@
a->storage_.SetAllocatedData(b_data);
a->storage_.SetAllocatedCapacity(b_capacity);
- if (a->storage_.GetAllocator() != b->storage_.GetAllocator()) {
- swap(a->storage_.GetAllocator(), b->storage_.GetAllocator());
+ if (*a->storage_.GetAllocPtr() != *b->storage_.GetAllocPtr()) {
+ swap(*a->storage_.GetAllocPtr(), *b->storage_.GetAllocPtr());
}
assert(b->size() == a_size);
diff --git a/absl/container/inlined_vector_benchmark.cc b/absl/container/inlined_vector_benchmark.cc
index 7bb3271..d906997 100644
--- a/absl/container/inlined_vector_benchmark.cc
+++ b/absl/container/inlined_vector_benchmark.cc
@@ -405,12 +405,6 @@
using NontrivialVec = absl::InlinedVector<NontrivialType, kInlineElements>;
-#define BENCHMARK_OPERATION(BM_Function) \
- BENCHMARK_TEMPLATE(BM_Function, TrivialVec, kSmallSize); \
- BENCHMARK_TEMPLATE(BM_Function, TrivialVec, kLargeSize); \
- BENCHMARK_TEMPLATE(BM_Function, NontrivialVec, kSmallSize); \
- BENCHMARK_TEMPLATE(BM_Function, NontrivialVec, kLargeSize)
-
template <typename VecT, typename PrepareVec, typename TestVec>
void BatchedBenchmark(benchmark::State& state, PrepareVec prepare_vec,
TestVec test_vec) {
@@ -432,13 +426,18 @@
}
}
-template <typename VecT, size_t Size>
+template <typename VecT, size_t FromSize>
void BM_Clear(benchmark::State& state) {
BatchedBenchmark<VecT>(
state,
- /* prepare_vec = */ [](VecT* vec) { vec->resize(Size); },
+ /* prepare_vec = */ [](VecT* vec) { vec->resize(FromSize); },
/* test_vec = */ [](VecT* vec) { vec->clear(); });
}
-BENCHMARK_OPERATION(BM_Clear);
+
+BENCHMARK_TEMPLATE(BM_Clear, TrivialVec, kSmallSize);
+BENCHMARK_TEMPLATE(BM_Clear, TrivialVec, kLargeSize);
+
+BENCHMARK_TEMPLATE(BM_Clear, NontrivialVec, kSmallSize);
+BENCHMARK_TEMPLATE(BM_Clear, NontrivialVec, kLargeSize);
} // namespace
diff --git a/absl/container/internal/inlined_vector.h b/absl/container/internal/inlined_vector.h
index 4589ce0..a2b3d24 100644
--- a/absl/container/internal/inlined_vector.h
+++ b/absl/container/internal/inlined_vector.h
@@ -33,7 +33,7 @@
std::forward_iterator_tag>;
template <typename AllocatorType, typename ValueType, typename SizeType>
-void DestroyElements(AllocatorType alloc, ValueType* destroy_first,
+void DestroyElements(AllocatorType* alloc_ptr, ValueType* destroy_first,
SizeType destroy_size) {
using AllocatorTraits = std::allocator_traits<AllocatorType>;
@@ -45,7 +45,7 @@
// NOTE: We assume destructors do not throw and thus make no attempt to roll
// back.
for (SizeType i = 0; i < destroy_size; ++i) {
- AllocatorTraits::destroy(alloc, destroy_first + i);
+ AllocatorTraits::destroy(*alloc_ptr, destroy_first + i);
}
#ifndef NDEBUG
@@ -104,10 +104,12 @@
return data_.allocated.allocated_capacity;
}
- allocator_type& GetAllocator() { return metadata_.template get<0>(); }
+ allocator_type* GetAllocPtr() {
+ return std::addressof(metadata_.template get<0>());
+ }
- const allocator_type& GetAllocator() const {
- return metadata_.template get<0>();
+ const allocator_type* GetAllocPtr() const {
+ return std::addressof(metadata_.template get<0>());
}
void SetAllocatedSize(size_type size) {
diff --git a/absl/flags/parse.cc b/absl/flags/parse.cc
index 263d9d6..5e53ccd 100644
--- a/absl/flags/parse.cc
+++ b/absl/flags/parse.cc
@@ -190,7 +190,7 @@
char buf[1024];
auto get_res = GetEnvironmentVariableA(var_name, buf, sizeof(buf));
if (get_res >= sizeof(buf)) {
- return "";
+ return false;
}
if (get_res == 0) {
diff --git a/absl/hash/hash_test.cc b/absl/hash/hash_test.cc
index 92c64ad..449e77b 100644
--- a/absl/hash/hash_test.cc
+++ b/absl/hash/hash_test.cc
@@ -470,7 +470,7 @@
struct NoOp {
template <typename HashCode>
friend HashCode AbslHashValue(HashCode h, NoOp n) {
- return std::move(h);
+ return h;
}
};
diff --git a/absl/strings/internal/str_format/arg.h b/absl/strings/internal/str_format/arg.h
index 4d48af0..5cb3a14 100644
--- a/absl/strings/internal/str_format/arg.h
+++ b/absl/strings/internal/str_format/arg.h
@@ -7,6 +7,7 @@
#include <cstdio>
#include <iomanip>
#include <limits>
+#include <memory>
#include <sstream>
#include <string>
#include <type_traits>
@@ -292,7 +293,7 @@
struct Manager<T, ByPointer> {
static Data SetValue(const T& value) {
Data data;
- data.ptr = &value;
+ data.ptr = std::addressof(value);
return data;
}
diff --git a/absl/strings/internal/str_format/convert_test.cc b/absl/strings/internal/str_format/convert_test.cc
index 3b4d4b0..814ccf4 100644
--- a/absl/strings/internal/str_format/convert_test.cc
+++ b/absl/strings/internal/str_format/convert_test.cc
@@ -367,6 +367,17 @@
INSTANTIATE_TYPED_TEST_CASE_P(TypedFormatConvertTestWithAllIntTypes,
TypedFormatConvertTest, AllIntTypes);
+TEST_F(FormatConvertTest, VectorBool) {
+ // Make sure vector<bool>'s values behave as bools.
+ std::vector<bool> v = {true, false};
+ const std::vector<bool> cv = {true, false};
+ EXPECT_EQ("1,0,1,0",
+ FormatPack(UntypedFormatSpecImpl("%d,%d,%d,%d"),
+ absl::Span<const FormatArgImpl>(
+ {FormatArgImpl(v[0]), FormatArgImpl(v[1]),
+ FormatArgImpl(cv[0]), FormatArgImpl(cv[1])})));
+}
+
TEST_F(FormatConvertTest, Uint128) {
absl::uint128 v = static_cast<absl::uint128>(0x1234567890abcdef) * 1979;
absl::uint128 max = absl::Uint128Max();
diff --git a/absl/strings/str_cat.cc b/absl/strings/str_cat.cc
index 2667976..ffe99db 100644
--- a/absl/strings/str_cat.cc
+++ b/absl/strings/str_cat.cc
@@ -89,7 +89,9 @@
// memcpy is allowed to overwrite arbitrary memory, so doing this after the
// call would force an extra fetch of x.size().
char* after = out + x.size();
- memcpy(out, x.data(), x.size());
+ if (x.size() != 0) {
+ memcpy(out, x.data(), x.size());
+ }
return after;
}
@@ -146,8 +148,10 @@
char* out = begin;
for (const absl::string_view piece : pieces) {
const size_t this_size = piece.size();
- memcpy(out, piece.data(), this_size);
- out += this_size;
+ if (this_size != 0) {
+ memcpy(out, piece.data(), this_size);
+ out += this_size;
+ }
}
assert(out == begin + result.size());
return result;
@@ -176,8 +180,10 @@
char* out = begin + old_size;
for (const absl::string_view piece : pieces) {
const size_t this_size = piece.size();
- memcpy(out, piece.data(), this_size);
- out += this_size;
+ if (this_size != 0) {
+ memcpy(out, piece.data(), this_size);
+ out += this_size;
+ }
}
assert(out == begin + dest->size());
}
diff --git a/absl/strings/str_cat_test.cc b/absl/strings/str_cat_test.cc
index 1f1051d..29db9c0 100644
--- a/absl/strings/str_cat_test.cc
+++ b/absl/strings/str_cat_test.cc
@@ -408,6 +408,20 @@
EXPECT_EQ(result, "1010");
}
+// Passing nullptr to memcpy is undefined behavior and this test
+// provides coverage of codepaths that handle empty strings with nullptrs.
+TEST(StrCat, AvoidsMemcpyWithNullptr) {
+ EXPECT_EQ(absl::StrCat(42, absl::string_view{}), "42");
+
+ // Cover CatPieces code.
+ EXPECT_EQ(absl::StrCat(1, 2, 3, 4, 5, absl::string_view{}), "12345");
+
+ // Cover AppendPieces.
+ std::string result;
+ absl::StrAppend(&result, 1, 2, 3, 4, 5, absl::string_view{});
+ EXPECT_EQ(result, "12345");
+}
+
#ifdef GTEST_HAS_DEATH_TEST
TEST(StrAppend, Death) {
std::string s = "self";
diff --git a/absl/strings/substitute.h b/absl/strings/substitute.h
index 507bc4f..32dec30 100644
--- a/absl/strings/substitute.h
+++ b/absl/strings/substitute.h
@@ -69,6 +69,8 @@
#include <cstring>
#include <string>
+#include <type_traits>
+#include <vector>
#include "absl/base/macros.h"
#include "absl/base/port.h"
@@ -151,6 +153,17 @@
Arg(Hex hex); // NOLINT(runtime/explicit)
Arg(Dec dec); // NOLINT(runtime/explicit)
+ // vector<bool>::reference and const_reference require special help to
+ // convert to `AlphaNum` because it requires two user defined conversions.
+ template <typename T,
+ absl::enable_if_t<
+ std::is_class<T>::value &&
+ (std::is_same<T, std::vector<bool>::reference>::value ||
+ std::is_same<T, std::vector<bool>::const_reference>::value)>* =
+ nullptr>
+ Arg(T value) // NOLINT(google-explicit-constructor)
+ : Arg(static_cast<bool>(value)) {}
+
// `void*` values, with the exception of `char*`, are printed as
// "0x<hex value>". However, in the case of `nullptr`, "NULL" is printed.
Arg(const void* value); // NOLINT(runtime/explicit)
diff --git a/absl/strings/substitute_test.cc b/absl/strings/substitute_test.cc
index f656890..e27abb1 100644
--- a/absl/strings/substitute_test.cc
+++ b/absl/strings/substitute_test.cc
@@ -15,6 +15,7 @@
#include "absl/strings/substitute.h"
#include <cstdint>
+#include <vector>
#include "gtest/gtest.h"
#include "absl/strings/str_cat.h"
@@ -172,6 +173,17 @@
EXPECT_EQ("a b c d e f g h i j", str);
}
+TEST(SubstituteTest, VectorBoolRef) {
+ std::vector<bool> v = {true, false};
+ const auto& cv = v;
+ EXPECT_EQ("true false true false",
+ absl::Substitute("$0 $1 $2 $3", v[0], v[1], cv[0], cv[1]));
+
+ std::string str = "Logic be like: ";
+ absl::SubstituteAndAppend(&str, "$0 $1 $2 $3", v[0], v[1], cv[0], cv[1]);
+ EXPECT_EQ("Logic be like: true false true false", str);
+}
+
#ifdef GTEST_HAS_DEATH_TEST
TEST(SubstituteDeathTest, SubstituteDeath) {
diff --git a/absl/types/optional.h b/absl/types/optional.h
index 1670022..142dc83 100644
--- a/absl/types/optional.h
+++ b/absl/types/optional.h
@@ -421,7 +421,9 @@
//
// Accesses the underlying `T` value of an `optional`. If the `optional` is
// empty, behavior is undefined.
- constexpr const T& operator*() const & { return reference(); }
+ constexpr const T& operator*() const& {
+ return ABSL_ASSERT(this->engaged_), reference();
+ }
T& operator*() & {
assert(this->engaged_);
return reference();