diff --git a/absl/debugging/failure_signal_handler.cc b/absl/debugging/failure_signal_handler.cc index affade3..8e75e54 100644 --- a/absl/debugging/failure_signal_handler.cc +++ b/absl/debugging/failure_signal_handler.cc
@@ -50,7 +50,9 @@ #ifndef _WIN32 #define ABSL_HAVE_SIGACTION // Apple WatchOS and TVOS don't allow sigaltstack -#if !(defined(TARGET_OS_WATCH) && TARGET_OS_WATCH) && \ +// Apple macOS has sigaltstack, but using it makes backtrace() unusable. +#if !(defined(TARGET_OS_OSX) && TARGET_OS_OSX) && \ + !(defined(TARGET_OS_WATCH) && TARGET_OS_WATCH) && \ !(defined(TARGET_OS_TV) && TARGET_OS_TV) && !defined(__QNX__) #define ABSL_HAVE_SIGALTSTACK #endif
diff --git a/absl/debugging/internal/demangle.cc b/absl/debugging/internal/demangle.cc index 93ae327..9ac644e 100644 --- a/absl/debugging/internal/demangle.cc +++ b/absl/debugging/internal/demangle.cc
@@ -548,6 +548,7 @@ static bool ParseCallOffset(State *state); static bool ParseNVOffset(State *state); static bool ParseVOffset(State *state); +static bool ParseAbiTags(State *state); static bool ParseCtorDtorName(State *state); static bool ParseDecltype(State *state); static bool ParseType(State *state); @@ -601,7 +602,7 @@ // // Reference: // - Itanium C++ ABI -// <https://mentorembedded.github.io/cxx-abi/abi.html#mangling> +// <https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling> // <mangled-name> ::= _Z <encoding> static bool ParseMangledName(State *state) { @@ -741,17 +742,42 @@ return true; } -// <unqualified-name> ::= <operator-name> -// ::= <ctor-dtor-name> -// ::= <source-name> -// ::= <local-source-name> // GCC extension; see below. -// ::= <unnamed-type-name> +// <unqualified-name> ::= <operator-name> [<abi-tags>] +// ::= <ctor-dtor-name> [<abi-tags>] +// ::= <source-name> [<abi-tags>] +// ::= <local-source-name> [<abi-tags>] +// ::= <unnamed-type-name> [<abi-tags>] +// +// <local-source-name> is a GCC extension; see below. static bool ParseUnqualifiedName(State *state) { ComplexityGuard guard(state); if (guard.IsTooComplex()) return false; - return (ParseOperatorName(state, nullptr) || ParseCtorDtorName(state) || - ParseSourceName(state) || ParseLocalSourceName(state) || - ParseUnnamedTypeName(state)); + if (ParseOperatorName(state, nullptr) || ParseCtorDtorName(state) || + ParseSourceName(state) || ParseLocalSourceName(state) || + ParseUnnamedTypeName(state)) { + return ParseAbiTags(state); + } + return false; +} + +// <abi-tags> ::= <abi-tag> [<abi-tags>] +// <abi-tag> ::= B <source-name> +static bool ParseAbiTags(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + + while (ParseOneCharToken(state, 'B')) { + ParseState copy = state->parse_state; + MaybeAppend(state, "[abi:"); + + if (!ParseSourceName(state)) { + state->parse_state = copy; + return false; + } + MaybeAppend(state, "]"); + } + + return true; } // <source-name> ::= <positive length number> <identifier>
diff --git a/absl/debugging/internal/demangle_test.cc b/absl/debugging/internal/demangle_test.cc index 6b14290..8463a2b 100644 --- a/absl/debugging/internal/demangle_test.cc +++ b/absl/debugging/internal/demangle_test.cc
@@ -102,6 +102,30 @@ EXPECT_FALSE(Demangle("_ZL3Foov.isra.2.constprop.", tmp, sizeof(tmp))); } +// Test the GNU abi_tag extension. +TEST(Demangle, AbiTags) { + char tmp[80]; + + // Mangled name generated via: + // struct [[gnu::abi_tag("abc")]] A{}; + // A a; + EXPECT_TRUE(Demangle("_Z1aB3abc", tmp, sizeof(tmp))); + EXPECT_STREQ("a[abi:abc]", tmp); + + // Mangled name generated via: + // struct B { + // B [[gnu::abi_tag("xyz")]] (){}; + // }; + // B b; + EXPECT_TRUE(Demangle("_ZN1BC2B3xyzEv", tmp, sizeof(tmp))); + EXPECT_STREQ("B::B[abi:xyz]()", tmp); + + // Mangled name generated via: + // [[gnu::abi_tag("foo", "bar")]] void C() {} + EXPECT_TRUE(Demangle("_Z1CB3barB3foov", tmp, sizeof(tmp))); + EXPECT_STREQ("C[abi:bar][abi:foo]()", tmp); +} + // Tests that verify that Demangle footprint is within some limit. // They are not to be run under sanitizers as the sanitizers increase // stack consumption by about 4x.
diff --git a/absl/debugging/internal/stacktrace_riscv-inl.inc b/absl/debugging/internal/stacktrace_riscv-inl.inc index 7123b71..70b11e5 100644 --- a/absl/debugging/internal/stacktrace_riscv-inl.inc +++ b/absl/debugging/internal/stacktrace_riscv-inl.inc
@@ -96,7 +96,8 @@ template <bool STRICT_UNWINDING, bool WITH_CONTEXT> ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS // May read random elements from stack. ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // May read random elements from stack. -static void ** NextStackFrame(void **old_frame_pointer, const void *uc) { +static void ** NextStackFrame(void **old_frame_pointer, const void *uc, + const std::pair<size_t, size_t> range) { // . // . // . @@ -159,7 +160,11 @@ const uintptr_t max_size = STRICT_UNWINDING ? 100000 : 1000000; const uintptr_t frame_size = ComputeStackFrameSize(old_frame_pointer, new_frame_pointer); - if (frame_size == kUnknownFrameSize || frame_size > max_size) + if (frame_size == kUnknownFrameSize && + (reinterpret_cast<uintptr_t>(new_frame_pointer) < range.first || + reinterpret_cast<uintptr_t>(new_frame_pointer) > range.second)) + return nullptr; + if (frame_size > max_size) return nullptr; } @@ -180,6 +185,12 @@ #error reading stack pointer not yet supported on this platform #endif + std::pair<size_t, size_t> stack = { + // assume that the first page is not the stack. + static_cast<size_t>(sysconf(_SC_PAGESIZE)), + std::numeric_limits<size_t>::max() - sizeof(void *) + }; + int n = 0; void *return_address = nullptr; while (frame_pointer && n < max_depth) { @@ -190,7 +201,8 @@ // non-strict unwinding rules to produce a stack trace that is as complete // as possible (even if it contains a few bogus entries in some rare cases). void **next_frame_pointer = - NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(frame_pointer, ucp); + NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(frame_pointer, ucp, + stack); if (skip_count > 0) { skip_count--; @@ -217,7 +229,8 @@ num_dropped_frames++; } frame_pointer = - NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(frame_pointer, ucp); + NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(frame_pointer, ucp, + stack); } *min_dropped_frames = num_dropped_frames; }
diff --git a/absl/debugging/symbolize_elf.inc b/absl/debugging/symbolize_elf.inc index 9bfdd91..fa401ae 100644 --- a/absl/debugging/symbolize_elf.inc +++ b/absl/debugging/symbolize_elf.inc
@@ -442,8 +442,8 @@ const off_t offset) { off_t off = lseek(fd, offset, SEEK_SET); if (off == (off_t)-1) { - ABSL_RAW_LOG(WARNING, "lseek(%d, %ju, SEEK_SET) failed: errno=%d", fd, - static_cast<uintmax_t>(offset), errno); + ABSL_RAW_LOG(WARNING, "lseek(%d, %jd, SEEK_SET) failed: errno=%d", fd, + static_cast<intmax_t>(offset), errno); return -1; } return ReadPersistent(fd, buf, count); @@ -489,12 +489,19 @@ (buf_bytes > num_bytes_left) ? num_bytes_left : buf_bytes; const off_t offset = sh_offset + i * sizeof(buf[0]); const ssize_t len = ReadFromOffset(fd, buf, num_bytes_to_read, offset); + if (len < 0) { + ABSL_RAW_LOG( + WARNING, + "Reading %zd bytes from offset %ju returned %zd which is negative.", + num_bytes_to_read, static_cast<intmax_t>(offset), len); + return false; + } if (len % sizeof(buf[0]) != 0) { ABSL_RAW_LOG( WARNING, - "Reading %zd bytes from offset %ju returned %zd which is not a " + "Reading %zd bytes from offset %jd returned %zd which is not a " "multiple of %zu.", - num_bytes_to_read, static_cast<uintmax_t>(offset), len, + num_bytes_to_read, static_cast<intmax_t>(offset), len, sizeof(buf[0])); return false; } @@ -693,6 +700,7 @@ const int entries_in_chunk = std::min(num_remaining_symbols, buf_entries); const int bytes_in_chunk = entries_in_chunk * sizeof(buf[0]); const ssize_t len = ReadFromOffset(fd, buf, bytes_in_chunk, offset); + SAFE_ASSERT(len >= 0); SAFE_ASSERT(len % sizeof(buf[0]) == 0); const ssize_t num_symbols_in_buf = len / sizeof(buf[0]); SAFE_ASSERT(num_symbols_in_buf <= entries_in_chunk);
diff --git a/absl/functional/any_invocable_test.cc b/absl/functional/any_invocable_test.cc index fb5e779..dabaae9 100644 --- a/absl/functional/any_invocable_test.cc +++ b/absl/functional/any_invocable_test.cc
@@ -1246,12 +1246,9 @@ #else using AnyInvType = typename TypeParam::AnyInvType; -// TODO(b/217761454): Fix this and re-enable for MSVC. -#ifndef _MSC_VER EXPECT_FALSE((std::is_constructible< AnyInvType, typename TypeParam::AnyInvocableFunTypeNotNoexcept*>::value)); -#endif EXPECT_FALSE(( std::is_constructible<AnyInvType, typename TypeParam::IncompatibleInvocable>::value)); @@ -1264,12 +1261,9 @@ #else using AnyInvType = typename TypeParam::AnyInvType; -// TODO(b/217761454): Fix this and re-enable for MSVC. -#ifndef _MSC_VER EXPECT_FALSE((std::is_assignable< AnyInvType&, typename TypeParam::AnyInvocableFunTypeNotNoexcept*>::value)); -#endif EXPECT_FALSE( (std::is_assignable<AnyInvType&, typename TypeParam::IncompatibleInvocable>::value));
diff --git a/absl/hash/internal/low_level_hash.cc b/absl/hash/internal/low_level_hash.cc index 6f9cb9c..d288f3e 100644 --- a/absl/hash/internal/low_level_hash.cc +++ b/absl/hash/internal/low_level_hash.cc
@@ -40,7 +40,7 @@ } uint64_t LowLevelHash(const void* data, size_t len, uint64_t seed, - const uint64_t salt[]) { + const uint64_t salt[5]) { const uint8_t* ptr = static_cast<const uint8_t*>(data); uint64_t starting_length = static_cast<uint64_t>(len); uint64_t current_state = seed ^ salt[0];
diff --git a/absl/strings/cord.cc b/absl/strings/cord.cc index 85a67a0..b34c03a 100644 --- a/absl/strings/cord.cc +++ b/absl/strings/cord.cc
@@ -537,18 +537,23 @@ } } -static CordBuffer CreateAppendBuffer(InlineData& data, size_t capacity) { +static CordBuffer CreateAppendBuffer(InlineData& data, size_t block_size, + size_t capacity) { // Watch out for overflow, people can ask for size_t::max(). const size_t size = data.inline_size(); - capacity = (std::min)(std::numeric_limits<size_t>::max() - size, capacity); - CordBuffer buffer = CordBuffer::CreateWithDefaultLimit(size + capacity); + const size_t max_capacity = std::numeric_limits<size_t>::max() - size; + capacity = (std::min)(max_capacity, capacity) + size; + CordBuffer buffer = + block_size ? CordBuffer::CreateWithCustomLimit(block_size, capacity) + : CordBuffer::CreateWithDefaultLimit(capacity); cord_internal::SmallMemmove(buffer.data(), data.as_chars(), size); buffer.SetLength(size); data = {}; return buffer; } -CordBuffer Cord::GetAppendBufferSlowPath(size_t capacity, size_t min_capacity) { +CordBuffer Cord::GetAppendBufferSlowPath(size_t block_size, size_t capacity, + size_t min_capacity) { auto constexpr method = CordzUpdateTracker::kGetAppendBuffer; CordRep* tree = contents_.tree(); if (tree != nullptr) { @@ -558,9 +563,10 @@ contents_.SetTreeOrEmpty(result.tree, scope); return CordBuffer(result.extracted->flat()); } - return CordBuffer::CreateWithDefaultLimit(capacity); + return block_size ? CordBuffer::CreateWithCustomLimit(block_size, capacity) + : CordBuffer::CreateWithDefaultLimit(capacity); } - return CreateAppendBuffer(contents_.data_, capacity); + return CreateAppendBuffer(contents_.data_, block_size, capacity); } void Cord::Append(const Cord& src) {
diff --git a/absl/strings/cord.h b/absl/strings/cord.h index 18d6ab8..0332671 100644 --- a/absl/strings/cord.h +++ b/absl/strings/cord.h
@@ -284,6 +284,19 @@ // } CordBuffer GetAppendBuffer(size_t capacity, size_t min_capacity = 16); + // Returns a CordBuffer, re-using potential existing capacity in this cord. + // + // This function is identical to `GetAppendBuffer`, except that in the case + // where a new `CordBuffer` is allocated, it is allocated using the provided + // custom limit instead of the default limit. `GetAppendBuffer` will default + // to `CordBuffer::CreateWithDefaultLimit(capacity)` whereas this method + // will default to `CordBuffer::CreateWithCustomLimit(block_size, capacity)`. + // This method is equivalent to `GetAppendBuffer` if `block_size` is zero. + // See the documentation for `CreateWithCustomLimit` for more details on the + // restrictions and legal values for `block_size`. + CordBuffer GetCustomAppendBuffer(size_t block_size, size_t capacity, + size_t min_capacity = 16); + // Cord::Prepend() // // Prepends data to the Cord, which may come from another Cord or other string @@ -861,9 +874,6 @@ void PrependTreeToTree(CordRep* tree, MethodIdentifier method); void PrependTree(CordRep* tree, MethodIdentifier method); - template <bool has_length> - void GetAppendRegion(char** region, size_t* size, size_t length); - bool IsSame(const InlineRep& other) const { return memcmp(&data_, &other.data_, sizeof(data_)) == 0; } @@ -980,7 +990,8 @@ void AppendPrecise(absl::string_view src, MethodIdentifier method); void PrependPrecise(absl::string_view src, MethodIdentifier method); - CordBuffer GetAppendBufferSlowPath(size_t capacity, size_t min_capacity); + CordBuffer GetAppendBufferSlowPath(size_t block_size, size_t capacity, + size_t min_capacity); // Prepends the provided data to this instance. `method` contains the public // API method for this action which is tracked for Cordz sampling purposes. @@ -1360,7 +1371,17 @@ inline CordBuffer Cord::GetAppendBuffer(size_t capacity, size_t min_capacity) { if (empty()) return CordBuffer::CreateWithDefaultLimit(capacity); - return GetAppendBufferSlowPath(capacity, min_capacity); + return GetAppendBufferSlowPath(0, capacity, min_capacity); +} + +inline CordBuffer Cord::GetCustomAppendBuffer(size_t block_size, + size_t capacity, + size_t min_capacity) { + if (empty()) { + return block_size ? CordBuffer::CreateWithCustomLimit(block_size, capacity) + : CordBuffer::CreateWithDefaultLimit(capacity); + } + return GetAppendBufferSlowPath(block_size, capacity, min_capacity); } extern template void Cord::Append(std::string&& src);
diff --git a/absl/strings/cord_test.cc b/absl/strings/cord_test.cc index 0862f69..d28ba11 100644 --- a/absl/strings/cord_test.cc +++ b/absl/strings/cord_test.cc
@@ -733,18 +733,48 @@ EXPECT_THAT(cord.Chunks(), ::testing::ElementsAre(s2, s1)); } -TEST_P(CordTest, GetAppendBufferOnEmptyCord) { +class CordAppendBufferTest : public testing::TestWithParam<bool> { + public: + size_t is_default() const { return GetParam(); } + + // Returns human readable string representation of the test parameter. + static std::string ToString(testing::TestParamInfo<bool> param) { + return param.param ? "DefaultLimit" : "CustomLimit"; + } + + size_t limit() const { + return is_default() ? absl::CordBuffer::kDefaultLimit + : absl::CordBuffer::kCustomLimit; + } + + size_t maximum_payload() const { + return is_default() ? absl::CordBuffer::MaximumPayload() + : absl::CordBuffer::MaximumPayload(limit()); + } + + absl::CordBuffer GetAppendBuffer(absl::Cord& cord, size_t capacity, + size_t min_capacity = 16) { + return is_default() + ? cord.GetAppendBuffer(capacity, min_capacity) + : cord.GetCustomAppendBuffer(limit(), capacity, min_capacity); + } +}; + +INSTANTIATE_TEST_SUITE_P(WithParam, CordAppendBufferTest, testing::Bool(), + CordAppendBufferTest::ToString); + +TEST_P(CordAppendBufferTest, GetAppendBufferOnEmptyCord) { absl::Cord cord; - absl::CordBuffer buffer = cord.GetAppendBuffer(1000); + absl::CordBuffer buffer = GetAppendBuffer(cord, 1000); EXPECT_GE(buffer.capacity(), 1000); EXPECT_EQ(buffer.length(), 0); } -TEST_P(CordTest, GetAppendBufferOnInlinedCord) { +TEST_P(CordAppendBufferTest, GetAppendBufferOnInlinedCord) { static constexpr int kInlinedSize = sizeof(absl::CordBuffer) - 1; for (int size : {6, kInlinedSize - 3, kInlinedSize - 2, 1000}) { absl::Cord cord("Abc"); - absl::CordBuffer buffer = cord.GetAppendBuffer(size, 1); + absl::CordBuffer buffer = GetAppendBuffer(cord, size, 1); EXPECT_GE(buffer.capacity(), 3 + size); EXPECT_EQ(buffer.length(), 3); EXPECT_EQ(absl::string_view(buffer.data(), buffer.length()), "Abc"); @@ -752,7 +782,7 @@ } } -TEST_P(CordTest, GetAppendBufferOnInlinedCordWithCapacityCloseToMax) { +TEST_P(CordAppendBufferTest, GetAppendBufferOnInlinedCordCapacityCloseToMax) { // Cover the use case where we have a non empty inlined cord with some size // 'n', and ask for something like 'uint64_max - k', assuming internal logic // could overflow on 'uint64_max - k + size', and return a valid, but @@ -760,30 +790,31 @@ for (size_t dist_from_max = 0; dist_from_max <= 4; ++dist_from_max) { absl::Cord cord("Abc"); size_t size = std::numeric_limits<size_t>::max() - dist_from_max; - absl::CordBuffer buffer = cord.GetAppendBuffer(size, 1); - EXPECT_EQ(buffer.capacity(), absl::CordBuffer::kDefaultLimit); + absl::CordBuffer buffer = GetAppendBuffer(cord, size, 1); + EXPECT_GE(buffer.capacity(), maximum_payload()); EXPECT_EQ(buffer.length(), 3); EXPECT_EQ(absl::string_view(buffer.data(), buffer.length()), "Abc"); EXPECT_TRUE(cord.empty()); } } -TEST_P(CordTest, GetAppendBufferOnFlat) { +TEST_P(CordAppendBufferTest, GetAppendBufferOnFlat) { // Create a cord with a single flat and extra capacity absl::Cord cord; absl::CordBuffer buffer = absl::CordBuffer::CreateWithDefaultLimit(500); + const size_t expected_capacity = buffer.capacity(); buffer.SetLength(3); memcpy(buffer.data(), "Abc", 3); cord.Append(std::move(buffer)); - buffer = cord.GetAppendBuffer(6); - EXPECT_GE(buffer.capacity(), 500); + buffer = GetAppendBuffer(cord, 6); + EXPECT_EQ(buffer.capacity(), expected_capacity); EXPECT_EQ(buffer.length(), 3); EXPECT_EQ(absl::string_view(buffer.data(), buffer.length()), "Abc"); EXPECT_TRUE(cord.empty()); } -TEST_P(CordTest, GetAppendBufferOnFlatWithoutMinCapacity) { +TEST_P(CordAppendBufferTest, GetAppendBufferOnFlatWithoutMinCapacity) { // Create a cord with a single flat and extra capacity absl::Cord cord; absl::CordBuffer buffer = absl::CordBuffer::CreateWithDefaultLimit(500); @@ -791,13 +822,13 @@ memset(buffer.data(), 'x', 30); cord.Append(std::move(buffer)); - buffer = cord.GetAppendBuffer(1000, 900); + buffer = GetAppendBuffer(cord, 1000, 900); EXPECT_GE(buffer.capacity(), 1000); EXPECT_EQ(buffer.length(), 0); EXPECT_EQ(cord, std::string(30, 'x')); } -TEST_P(CordTest, GetAppendBufferOnTree) { +TEST_P(CordAppendBufferTest, GetAppendBufferOnTree) { RandomEngine rng; for (int num_flats : {2, 3, 100}) { // Create a cord with `num_flats` flats and extra capacity @@ -812,7 +843,7 @@ memcpy(buffer.data(), last.data(), 10); cord.Append(std::move(buffer)); } - absl::CordBuffer buffer = cord.GetAppendBuffer(6); + absl::CordBuffer buffer = GetAppendBuffer(cord, 6); EXPECT_GE(buffer.capacity(), 500); EXPECT_EQ(buffer.length(), 10); EXPECT_EQ(absl::string_view(buffer.data(), buffer.length()), last); @@ -820,7 +851,7 @@ } } -TEST_P(CordTest, GetAppendBufferOnTreeWithoutMinCapacity) { +TEST_P(CordAppendBufferTest, GetAppendBufferOnTreeWithoutMinCapacity) { absl::Cord cord; for (int i = 0; i < 2; ++i) { absl::CordBuffer buffer = absl::CordBuffer::CreateWithDefaultLimit(500); @@ -828,13 +859,13 @@ memcpy(buffer.data(), i ? "def" : "Abc", 3); cord.Append(std::move(buffer)); } - absl::CordBuffer buffer = cord.GetAppendBuffer(1000, 900); + absl::CordBuffer buffer = GetAppendBuffer(cord, 1000, 900); EXPECT_GE(buffer.capacity(), 1000); EXPECT_EQ(buffer.length(), 0); EXPECT_EQ(cord, "Abcdef"); } -TEST_P(CordTest, GetAppendBufferOnSubstring) { +TEST_P(CordAppendBufferTest, GetAppendBufferOnSubstring) { // Create a large cord with a single flat and some extra capacity absl::Cord cord; absl::CordBuffer buffer = absl::CordBuffer::CreateWithDefaultLimit(500); @@ -844,12 +875,12 @@ cord.RemovePrefix(1); // Deny on substring - buffer = cord.GetAppendBuffer(6); + buffer = GetAppendBuffer(cord, 6); EXPECT_EQ(buffer.length(), 0); EXPECT_EQ(cord, std::string(449, 'x')); } -TEST_P(CordTest, GetAppendBufferOnSharedCord) { +TEST_P(CordAppendBufferTest, GetAppendBufferOnSharedCord) { // Create a shared cord with a single flat and extra capacity absl::Cord cord; absl::CordBuffer buffer = absl::CordBuffer::CreateWithDefaultLimit(500); @@ -859,7 +890,7 @@ absl::Cord shared_cord = cord; // Deny on flat - buffer = cord.GetAppendBuffer(6); + buffer = GetAppendBuffer(cord, 6); EXPECT_EQ(buffer.length(), 0); EXPECT_EQ(cord, "Abc"); @@ -870,7 +901,7 @@ shared_cord = cord; // Deny on tree - buffer = cord.GetAppendBuffer(6); + buffer = GetAppendBuffer(cord, 6); EXPECT_EQ(buffer.length(), 0); EXPECT_EQ(cord, "Abcdef"); }
diff --git a/absl/strings/escaping.cc b/absl/strings/escaping.cc index 18b20b8..4dc6970 100644 --- a/absl/strings/escaping.cc +++ b/absl/strings/escaping.cc
@@ -773,7 +773,8 @@ const signed char* unbase64) { // Determine the size of the output string. Base64 encodes every 3 bytes into // 4 characters. any leftover chars are added directly for good measure. - // This is documented in the base64 RFC: http://tools.ietf.org/html/rfc3548 + // This is documented in the base64 RFC: + // https://datatracker.ietf.org/doc/html/rfc3548 const size_t dest_len = 3 * (slen / 4) + (slen % 4); strings_internal::STLStringResizeUninitialized(dest, dest_len); @@ -876,8 +877,8 @@ // WebSafeBase64Escape() - Google's variation of base64 encoder // // Check out -// http://tools.ietf.org/html/rfc2045 for formal description, but what we -// care about is that... +// https://datatracker.ietf.org/doc/html/rfc2045 for formal description, but +// what we care about is that... // Take the encoded stuff in groups of 4 characters and turn each // character into a code 0 to 63 thus: // A-Z map to 0 to 25
diff --git a/absl/types/internal/span.h b/absl/types/internal/span.h index 112612f..1920a89 100644 --- a/absl/types/internal/span.h +++ b/absl/types/internal/span.h
@@ -28,6 +28,9 @@ namespace absl { ABSL_NAMESPACE_BEGIN +template <typename T> +class Span; + namespace span_internal { // A constexpr min function constexpr size_t Min(size_t a, size_t b) noexcept { return a < b ? a : b; } @@ -121,6 +124,36 @@ template <typename From, typename To> using EnableIfConvertibleTo = typename std::enable_if<IsConvertible<From, To>::value>::type; + +// IsView is true for types where the return type of .data() is the same for +// mutable and const instances. This isn't foolproof, but it's only used to +// enable a compiler warning. +template <typename T, typename = void, typename = void> +struct IsView { + static constexpr bool value = false; +}; + +template <typename T> +struct IsView< + T, absl::void_t<decltype(span_internal::GetData(std::declval<const T&>()))>, + absl::void_t<decltype(span_internal::GetData(std::declval<T&>()))>> { + private: + using Container = std::remove_const_t<T>; + using ConstData = + decltype(span_internal::GetData(std::declval<const Container&>())); + using MutData = decltype(span_internal::GetData(std::declval<Container&>())); + public: + static constexpr bool value = std::is_same<ConstData, MutData>::value; +}; + +// These enablers result in 'int' so they can be used as typenames or defaults +// in template paramters lists. +template <typename T> +using EnableIfIsView = std::enable_if_t<IsView<T>::value, int>; + +template <typename T> +using EnableIfNotIsView = std::enable_if_t<!IsView<T>::value, int>; + } // namespace span_internal ABSL_NAMESPACE_END } // namespace absl
diff --git a/absl/types/span.h b/absl/types/span.h index fdfbd77..cd863a9 100644 --- a/absl/types/span.h +++ b/absl/types/span.h
@@ -60,6 +60,7 @@ #include <type_traits> #include <utility> +#include "absl/base/attributes.h" #include "absl/base/internal/throw_delegate.h" #include "absl/base/macros.h" #include "absl/base/optimization.h" @@ -160,12 +161,12 @@ // Used to SFINAE-enable a function when the slice elements are const. template <typename U> - using EnableIfConstView = + using EnableIfValueIsConst = typename std::enable_if<std::is_const<T>::value, U>::type; // Used to SFINAE-enable a function when the slice elements are mutable. template <typename U> - using EnableIfMutableView = + using EnableIfValueIsMutable = typename std::enable_if<!std::is_const<T>::value, U>::type; public: @@ -196,13 +197,34 @@ // Explicit reference constructor for a mutable `Span<T>` type. Can be // replaced with MakeSpan() to infer the type parameter. template <typename V, typename = EnableIfConvertibleFrom<V>, - typename = EnableIfMutableView<V>> - explicit Span(V& v) noexcept // NOLINT(runtime/references) + typename = EnableIfValueIsMutable<V>, + typename = span_internal::EnableIfNotIsView<V>> + explicit Span( + V& v + ABSL_ATTRIBUTE_LIFETIME_BOUND) noexcept // NOLINT(runtime/references) : Span(span_internal::GetData(v), v.size()) {} // Implicit reference constructor for a read-only `Span<const T>` type template <typename V, typename = EnableIfConvertibleFrom<V>, - typename = EnableIfConstView<V>> + typename = EnableIfValueIsConst<V>, + typename = span_internal::EnableIfNotIsView<V>> + constexpr Span( + const V& v + ABSL_ATTRIBUTE_LIFETIME_BOUND) noexcept // NOLINT(runtime/explicit) + : Span(span_internal::GetData(v), v.size()) {} + + // Overloads of the above two functions that are only enabled for view types. + // This is so we can drop the ABSL_ATTRIBUTE_LIFETIME_BOUND annotation. These + // overloads must be made unique by using a different template parameter list + // (hence the = 0 for the IsView enabler). + template <typename V, typename = EnableIfConvertibleFrom<V>, + typename = EnableIfValueIsMutable<V>, + span_internal::EnableIfIsView<V> = 0> + explicit Span(V& v) noexcept // NOLINT(runtime/references) + : Span(span_internal::GetData(v), v.size()) {} + template <typename V, typename = EnableIfConvertibleFrom<V>, + typename = EnableIfValueIsConst<V>, + span_internal::EnableIfIsView<V> = 0> constexpr Span(const V& v) noexcept // NOLINT(runtime/explicit) : Span(span_internal::GetData(v), v.size()) {} @@ -242,7 +264,7 @@ // Process(ints); // template <typename LazyT = T, - typename = EnableIfConstView<LazyT>> + typename = EnableIfValueIsConst<LazyT>> Span(std::initializer_list<value_type> v ABSL_ATTRIBUTE_LIFETIME_BOUND) noexcept // NOLINT(runtime/explicit) : Span(v.begin(), v.size()) {}
diff --git a/absl/utility/utility_test.cc b/absl/utility/utility_test.cc index f044ad6..2f0509a 100644 --- a/absl/utility/utility_test.cc +++ b/absl/utility/utility_test.cc
@@ -1,4 +1,4 @@ -// Copyright 2017 The Abseil Authors. +// 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. @@ -14,10 +14,12 @@ #include "absl/utility/utility.h" +#include <memory> #include <sstream> #include <string> #include <tuple> #include <type_traits> +#include <utility> #include <vector> #include "gmock/gmock.h" @@ -35,10 +37,10 @@ // 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 +#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; @@ -227,8 +229,7 @@ } TEST(ApplyTest, NonCopyableResult) { - EXPECT_THAT(absl::apply(Factory, std::make_tuple(42)), - ::testing::Pointee(42)); + EXPECT_THAT(absl::apply(Factory, std::make_tuple(42)), Pointee(42)); } TEST(ApplyTest, VoidResult) { absl::apply(NoOp, std::tuple<>()); } @@ -373,4 +374,3 @@ } } // namespace -