修改pods

This commit is contained in:
2025-09-20 17:13:38 +08:00
parent 7787b3ee30
commit 28ff2b0264
5251 changed files with 345029 additions and 285168 deletions

View File

@@ -109,7 +109,17 @@ class BigUnsigned {
size_ = (std::min)(size_ + word_shift, max_words);
count %= 32;
if (count == 0) {
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=warray-bounds
// shows a lot of bogus -Warray-bounds warnings under GCC.
// This is not the only one in Abseil.
#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(14, 0)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Warray-bounds"
#endif
std::copy_backward(words_, words_ + size_ - word_shift, words_ + size_);
#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(14, 0)
#pragma GCC diagnostic pop
#endif
} else {
for (int i = (std::min)(size_, max_words - 1); i > word_shift; --i) {
words_[i] = (words_[i - word_shift] << count) |

View File

@@ -85,7 +85,7 @@ enum Constants {
};
// Emits a fatal error "Unexpected node type: xyz" and aborts the program.
ABSL_ATTRIBUTE_NORETURN void LogFatalNodeType(CordRep* rep);
[[noreturn]] void LogFatalNodeType(CordRep* rep);
// Fast implementation of memmove for up to 15 bytes. This implementation is
// safe for overlapping regions. If nullify_tail is true, the destination is
@@ -259,7 +259,7 @@ struct CordRep {
// on the specific layout of these fields. Notably: the non-trivial field
// `refcount` being preceded by `length`, and being tailed by POD data
// members only.
// # LINT.IfChange
// LINT.IfChange
size_t length;
RefcountAndFlags refcount;
// If tag < FLAT, it represents CordRepKind and indicates the type of node.
@@ -275,7 +275,7 @@ struct CordRep {
// allocate room for these in the derived class, as not all compilers reuse
// padding space from the base class (clang and gcc do, MSVC does not, etc)
uint8_t storage[3];
// # LINT.ThenChange(cord_rep_btree.h:copy_raw)
// LINT.ThenChange(cord_rep_btree.h:copy_raw)
// Returns true if this instance's tag matches the requested type.
constexpr bool IsSubstring() const { return tag == SUBSTRING; }
@@ -352,18 +352,19 @@ struct CordRepExternal : public CordRep {
static void Delete(CordRep* rep);
};
struct Rank1 {};
struct Rank0 : Rank1 {};
// Use go/ranked-overloads for dispatching.
struct Rank0 {};
struct Rank1 : Rank0 {};
template <typename Releaser, typename = ::absl::base_internal::invoke_result_t<
Releaser, absl::string_view>>
void InvokeReleaser(Rank0, Releaser&& releaser, absl::string_view data) {
void InvokeReleaser(Rank1, Releaser&& releaser, absl::string_view data) {
::absl::base_internal::invoke(std::forward<Releaser>(releaser), data);
}
template <typename Releaser,
typename = ::absl::base_internal::invoke_result_t<Releaser>>
void InvokeReleaser(Rank1, Releaser&& releaser, absl::string_view) {
void InvokeReleaser(Rank0, Releaser&& releaser, absl::string_view) {
::absl::base_internal::invoke(std::forward<Releaser>(releaser));
}
@@ -381,7 +382,7 @@ struct CordRepExternalImpl
}
~CordRepExternalImpl() {
InvokeReleaser(Rank0{}, std::move(this->template get<0>()),
InvokeReleaser(Rank1{}, std::move(this->template get<0>()),
absl::string_view(base, length));
}
@@ -398,7 +399,6 @@ inline CordRepSubstring* CordRepSubstring::Create(CordRep* child, size_t pos,
assert(pos < child->length);
assert(n <= child->length - pos);
// TODO(b/217376272): Harden internal logic.
// Move to strategical places inside the Cord logic and make this an assert.
if (ABSL_PREDICT_FALSE(!(child->IsExternal() || child->IsFlat()))) {
LogFatalNodeType(child);
@@ -520,6 +520,7 @@ class InlineData {
constexpr InlineData(const InlineData& rhs) noexcept;
InlineData& operator=(const InlineData& rhs) noexcept;
friend void swap(InlineData& lhs, InlineData& rhs) noexcept;
friend bool operator==(const InlineData& lhs, const InlineData& rhs) {
#ifdef ABSL_INTERNAL_CORD_HAVE_SANITIZER
@@ -770,6 +771,12 @@ class InlineData {
char data[kMaxInline + 1];
AsTree as_tree;
};
// TODO(b/145829486): see swap(InlineData, InlineData) for more info.
inline void SwapValue(Rep rhs, Rep& refrhs) {
memcpy(&refrhs, this, sizeof(*this));
memcpy(this, &rhs, sizeof(*this));
}
};
// Private implementation of `Compare()`
@@ -884,6 +891,19 @@ inline void CordRep::Unref(CordRep* rep) {
}
}
inline void swap(InlineData& lhs, InlineData& rhs) noexcept {
lhs.unpoison();
rhs.unpoison();
// TODO(b/145829486): `std::swap(lhs.rep_, rhs.rep_)` results in bad codegen
// on clang, spilling the temporary swap value on the stack. Since `Rep` is
// trivial, we can make clang DTRT by calling a hand-rolled `SwapValue` where
// we pass `rhs` both by value (register allocated) and by reference. The IR
// then folds and inlines correctly into an optimized swap without spill.
lhs.rep_.SwapValue(rhs.rep_, rhs.rep_);
rhs.poison();
lhs.poison();
}
} // namespace cord_internal
ABSL_NAMESPACE_END

View File

@@ -684,14 +684,14 @@ inline CordRepBtree* CordRepBtree::CopyRaw(size_t new_length) const {
// except `refcount` is trivially copyable, and the compiler does not
// efficiently coalesce member-wise copy of these members.
// See https://gcc.godbolt.org/z/qY8zsca6z
// # LINT.IfChange(copy_raw)
// LINT.IfChange(copy_raw)
tree->length = new_length;
uint8_t* dst = &tree->tag;
const uint8_t* src = &tag;
const ptrdiff_t offset = src - reinterpret_cast<const uint8_t*>(this);
memcpy(dst, src, sizeof(CordRepBtree) - static_cast<size_t>(offset));
return tree;
// # LINT.ThenChange()
// LINT.ThenChange()
}
inline CordRepBtree* CordRepBtree::Copy() const {

View File

@@ -40,13 +40,15 @@ std::atomic<int> g_cordz_mean_interval(50000);
// Special negative 'not initialized' per thread value for cordz_next_sample.
static constexpr int64_t kInitCordzNextSample = -1;
ABSL_CONST_INIT thread_local int64_t cordz_next_sample = kInitCordzNextSample;
ABSL_CONST_INIT thread_local SamplingState cordz_next_sample = {
kInitCordzNextSample, 1};
// kIntervalIfDisabled is the number of profile-eligible events need to occur
// before the code will confirm that cordz is still disabled.
constexpr int64_t kIntervalIfDisabled = 1 << 16;
ABSL_ATTRIBUTE_NOINLINE bool cordz_should_profile_slow() {
ABSL_ATTRIBUTE_NOINLINE int64_t
cordz_should_profile_slow(SamplingState& state) {
thread_local absl::profiling_internal::ExponentialBiased
exponential_biased_generator;
@@ -55,30 +57,34 @@ ABSL_ATTRIBUTE_NOINLINE bool cordz_should_profile_slow() {
// Check if we disabled profiling. If so, set the next sample to a "large"
// number to minimize the overhead of the should_profile codepath.
if (mean_interval <= 0) {
cordz_next_sample = kIntervalIfDisabled;
return false;
state = {kIntervalIfDisabled, kIntervalIfDisabled};
return 0;
}
// Check if we're always sampling.
if (mean_interval == 1) {
cordz_next_sample = 1;
return true;
state = {1, 1};
return 1;
}
if (cordz_next_sample <= 0) {
if (cordz_next_sample.next_sample <= 0) {
// If first check on current thread, check cordz_should_profile()
// again using the created (initial) stride in cordz_next_sample.
const bool initialized = cordz_next_sample != kInitCordzNextSample;
cordz_next_sample = exponential_biased_generator.GetStride(mean_interval);
return initialized || cordz_should_profile();
const bool initialized =
cordz_next_sample.next_sample != kInitCordzNextSample;
auto old_stride = state.sample_stride;
auto stride = exponential_biased_generator.GetStride(mean_interval);
state = {stride, stride};
bool should_sample = initialized || cordz_should_profile() > 0;
return should_sample ? old_stride : 0;
}
--cordz_next_sample;
return false;
--state.next_sample;
return 0;
}
void cordz_set_next_sample_for_testing(int64_t next_sample) {
cordz_next_sample = next_sample;
cordz_next_sample = {next_sample, next_sample};
}
#endif // ABSL_INTERNAL_CORDZ_ENABLED

View File

@@ -41,23 +41,33 @@ void set_cordz_mean_interval(int32_t mean_interval);
#ifdef ABSL_INTERNAL_CORDZ_ENABLED
struct SamplingState {
int64_t next_sample;
int64_t sample_stride;
};
// cordz_next_sample is the number of events until the next sample event. If
// the value is 1 or less, the code will check on the next event if cordz is
// enabled, and if so, will sample the Cord. cordz is only enabled when we can
// use thread locals.
ABSL_CONST_INIT extern thread_local int64_t cordz_next_sample;
ABSL_CONST_INIT extern thread_local SamplingState cordz_next_sample;
// Determines if the next sample should be profiled. If it is, the value pointed
// at by next_sample will be set with the interval until the next sample.
bool cordz_should_profile_slow();
// Determines if the next sample should be profiled.
// Returns:
// 0: Do not sample
// >0: Sample with the stride of the last sampling period
int64_t cordz_should_profile_slow(SamplingState& state);
// Returns true if the next cord should be sampled.
inline bool cordz_should_profile() {
if (ABSL_PREDICT_TRUE(cordz_next_sample > 1)) {
cordz_next_sample--;
return false;
// Determines if the next sample should be profiled.
// Returns:
// 0: Do not sample
// >0: Sample with the stride of the last sampling period
inline int64_t cordz_should_profile() {
if (ABSL_PREDICT_TRUE(cordz_next_sample.next_sample > 1)) {
cordz_next_sample.next_sample--;
return 0;
}
return cordz_should_profile_slow();
return cordz_should_profile_slow(cordz_next_sample);
}
// Sets the interval until the next sample (for testing only)
@@ -65,7 +75,7 @@ void cordz_set_next_sample_for_testing(int64_t next_sample);
#else // ABSL_INTERNAL_CORDZ_ENABLED
inline bool cordz_should_profile() { return false; }
inline int64_t cordz_should_profile() { return 0; }
inline void cordz_set_next_sample_for_testing(int64_t) {}
#endif // ABSL_INTERNAL_CORDZ_ENABLED

View File

@@ -16,6 +16,7 @@
#include <atomic>
#include "absl/base/internal/raw_logging.h" // For ABSL_RAW_CHECK
#include "absl/base/no_destructor.h"
#include "absl/synchronization/mutex.h"
namespace absl {
@@ -43,33 +44,32 @@ struct Queue {
}
};
static Queue* GlobalQueue() {
static Queue* global_queue = new Queue;
return global_queue;
static Queue& GlobalQueue() {
static absl::NoDestructor<Queue> global_queue;
return *global_queue;
}
} // namespace
CordzHandle::CordzHandle(bool is_snapshot) : is_snapshot_(is_snapshot) {
Queue* global_queue = GlobalQueue();
Queue& global_queue = GlobalQueue();
if (is_snapshot) {
MutexLock lock(&global_queue->mutex);
CordzHandle* dq_tail =
global_queue->dq_tail.load(std::memory_order_acquire);
MutexLock lock(&global_queue.mutex);
CordzHandle* dq_tail = global_queue.dq_tail.load(std::memory_order_acquire);
if (dq_tail != nullptr) {
dq_prev_ = dq_tail;
dq_tail->dq_next_ = this;
}
global_queue->dq_tail.store(this, std::memory_order_release);
global_queue.dq_tail.store(this, std::memory_order_release);
}
}
CordzHandle::~CordzHandle() {
Queue* global_queue = GlobalQueue();
Queue& global_queue = GlobalQueue();
if (is_snapshot_) {
std::vector<CordzHandle*> to_delete;
{
MutexLock lock(&global_queue->mutex);
MutexLock lock(&global_queue.mutex);
CordzHandle* next = dq_next_;
if (dq_prev_ == nullptr) {
// We were head of the queue, delete every CordzHandle until we reach
@@ -85,7 +85,7 @@ CordzHandle::~CordzHandle() {
if (next) {
next->dq_prev_ = dq_prev_;
} else {
global_queue->dq_tail.store(dq_prev_, std::memory_order_release);
global_queue.dq_tail.store(dq_prev_, std::memory_order_release);
}
}
for (CordzHandle* handle : to_delete) {
@@ -95,20 +95,20 @@ CordzHandle::~CordzHandle() {
}
bool CordzHandle::SafeToDelete() const {
return is_snapshot_ || GlobalQueue()->IsEmpty();
return is_snapshot_ || GlobalQueue().IsEmpty();
}
void CordzHandle::Delete(CordzHandle* handle) {
assert(handle);
if (handle) {
Queue* const queue = GlobalQueue();
Queue& queue = GlobalQueue();
if (!handle->SafeToDelete()) {
MutexLock lock(&queue->mutex);
CordzHandle* dq_tail = queue->dq_tail.load(std::memory_order_acquire);
MutexLock lock(&queue.mutex);
CordzHandle* dq_tail = queue.dq_tail.load(std::memory_order_acquire);
if (dq_tail != nullptr) {
handle->dq_prev_ = dq_tail;
dq_tail->dq_next_ = handle;
queue->dq_tail.store(handle, std::memory_order_release);
queue.dq_tail.store(handle, std::memory_order_release);
return;
}
}
@@ -118,9 +118,9 @@ void CordzHandle::Delete(CordzHandle* handle) {
std::vector<const CordzHandle*> CordzHandle::DiagnosticsGetDeleteQueue() {
std::vector<const CordzHandle*> handles;
Queue* global_queue = GlobalQueue();
MutexLock lock(&global_queue->mutex);
CordzHandle* dq_tail = global_queue->dq_tail.load(std::memory_order_acquire);
Queue& global_queue = GlobalQueue();
MutexLock lock(&global_queue.mutex);
CordzHandle* dq_tail = global_queue.dq_tail.load(std::memory_order_acquire);
for (const CordzHandle* p = dq_tail; p; p = p->dq_prev_) {
handles.push_back(p);
}
@@ -133,9 +133,9 @@ bool CordzHandle::DiagnosticsHandleIsSafeToInspect(
if (handle == nullptr) return true;
if (handle->is_snapshot_) return false;
bool snapshot_found = false;
Queue* global_queue = GlobalQueue();
MutexLock lock(&global_queue->mutex);
for (const CordzHandle* p = global_queue->dq_tail; p; p = p->dq_prev_) {
Queue& global_queue = GlobalQueue();
MutexLock lock(&global_queue.mutex);
for (const CordzHandle* p = global_queue.dq_tail; p; p = p->dq_prev_) {
if (p == handle) return !snapshot_found;
if (p == this) snapshot_found = true;
}
@@ -150,8 +150,8 @@ CordzHandle::DiagnosticsGetSafeToInspectDeletedHandles() {
return handles;
}
Queue* global_queue = GlobalQueue();
MutexLock lock(&global_queue->mutex);
Queue& global_queue = GlobalQueue();
MutexLock lock(&global_queue.mutex);
for (const CordzHandle* p = dq_next_; p != nullptr; p = p->dq_next_) {
if (!p->is_snapshot()) {
handles.push_back(p);

View File

@@ -14,6 +14,8 @@
#include "absl/strings/internal/cordz_info.h"
#include <cstdint>
#include "absl/base/config.h"
#include "absl/base/internal/spinlock.h"
#include "absl/container/inlined_vector.h"
@@ -247,10 +249,12 @@ CordzInfo* CordzInfo::Next(const CordzSnapshot& snapshot) const {
return next;
}
void CordzInfo::TrackCord(InlineData& cord, MethodIdentifier method) {
void CordzInfo::TrackCord(InlineData& cord, MethodIdentifier method,
int64_t sampling_stride) {
assert(cord.is_tree());
assert(!cord.is_profiled());
CordzInfo* cordz_info = new CordzInfo(cord.as_tree(), nullptr, method);
CordzInfo* cordz_info =
new CordzInfo(cord.as_tree(), nullptr, method, sampling_stride);
cord.set_cordz_info(cordz_info);
cordz_info->Track();
}
@@ -266,7 +270,8 @@ void CordzInfo::TrackCord(InlineData& cord, const InlineData& src,
if (cordz_info != nullptr) cordz_info->Untrack();
// Start new cord sample
cordz_info = new CordzInfo(cord.as_tree(), src.cordz_info(), method);
cordz_info = new CordzInfo(cord.as_tree(), src.cordz_info(), method,
src.cordz_info()->sampling_stride());
cord.set_cordz_info(cordz_info);
cordz_info->Track();
}
@@ -298,9 +303,8 @@ size_t CordzInfo::FillParentStack(const CordzInfo* src, void** stack) {
return src->stack_depth_;
}
CordzInfo::CordzInfo(CordRep* rep,
const CordzInfo* src,
MethodIdentifier method)
CordzInfo::CordzInfo(CordRep* rep, const CordzInfo* src,
MethodIdentifier method, int64_t sampling_stride)
: rep_(rep),
stack_depth_(
static_cast<size_t>(absl::GetStackTrace(stack_,
@@ -309,7 +313,8 @@ CordzInfo::CordzInfo(CordRep* rep,
parent_stack_depth_(FillParentStack(src, parent_stack_)),
method_(method),
parent_method_(GetParentMethod(src)),
create_time_(absl::Now()) {
create_time_(absl::Now()),
sampling_stride_(sampling_stride) {
update_tracker_.LossyAdd(method);
if (src) {
// Copy parent counters.

View File

@@ -60,7 +60,8 @@ class ABSL_LOCKABLE CordzInfo : public CordzHandle {
// and/or deleted. `method` identifies the Cord public API method initiating
// the cord to be sampled.
// Requires `cord` to hold a tree, and `cord.cordz_info()` to be null.
static void TrackCord(InlineData& cord, MethodIdentifier method);
static void TrackCord(InlineData& cord, MethodIdentifier method,
int64_t sampling_stride);
// Identical to TrackCord(), except that this function fills the
// `parent_stack` and `parent_method` properties of the returned CordzInfo
@@ -181,6 +182,8 @@ class ABSL_LOCKABLE CordzInfo : public CordzHandle {
// or RemovePrefix.
CordzStatistics GetCordzStatistics() const;
int64_t sampling_stride() const { return sampling_stride_; }
private:
using SpinLock = absl::base_internal::SpinLock;
using SpinLockHolder = ::absl::base_internal::SpinLockHolder;
@@ -199,7 +202,7 @@ class ABSL_LOCKABLE CordzInfo : public CordzHandle {
static constexpr size_t kMaxStackDepth = 64;
explicit CordzInfo(CordRep* rep, const CordzInfo* src,
MethodIdentifier method);
MethodIdentifier method, int64_t weight);
~CordzInfo() override;
// Sets `rep_` without holding a lock.
@@ -250,12 +253,14 @@ class ABSL_LOCKABLE CordzInfo : public CordzHandle {
const MethodIdentifier parent_method_;
CordzUpdateTracker update_tracker_;
const absl::Time create_time_;
const int64_t sampling_stride_;
};
inline ABSL_ATTRIBUTE_ALWAYS_INLINE void CordzInfo::MaybeTrackCord(
InlineData& cord, MethodIdentifier method) {
if (ABSL_PREDICT_FALSE(cordz_should_profile())) {
TrackCord(cord, method);
auto stride = cordz_should_profile();
if (ABSL_PREDICT_FALSE(stride > 0)) {
TrackCord(cord, method, stride);
}
}

View File

@@ -14,6 +14,8 @@
#include "absl/strings/internal/escaping.h"
#include <limits>
#include "absl/base/internal/endian.h"
#include "absl/base/internal/raw_logging.h"
@@ -31,12 +33,14 @@ ABSL_CONST_INIT const char kBase64Chars[] =
ABSL_CONST_INIT const char kWebSafeBase64Chars[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
size_t CalculateBase64EscapedLenInternal(size_t input_len, bool do_padding) {
// Base64 encodes three bytes of input at a time. If the input is not
// divisible by three, we pad as appropriate.
//
// Base64 encodes each three bytes of input into four bytes of output.
constexpr size_t kMaxSize = (std::numeric_limits<size_t>::max() - 1) / 4 * 3;
ABSL_INTERNAL_CHECK(input_len <= kMaxSize,
"CalculateBase64EscapedLenInternal() overflow");
size_t len = (input_len / 3) * 4;
// Since all base 64 input is an integral number of octets, only the following
@@ -66,7 +70,6 @@ size_t CalculateBase64EscapedLenInternal(size_t input_len, bool do_padding) {
}
}
assert(len >= input_len); // make sure we didn't overflow
return len;
}

View File

@@ -1,44 +0,0 @@
// Copyright 2024 The Abseil Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef ABSL_STRINGS_INTERNAL_HAS_ABSL_STRINGIFY_H_
#define ABSL_STRINGS_INTERNAL_HAS_ABSL_STRINGIFY_H_
#include "absl/strings/has_absl_stringify.h"
#include "absl/base/config.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace strings_internal {
// This exists to fix a circular dependency problem with the GoogleTest release.
// GoogleTest referenced this internal file and this internal trait. Since
// simultaneous releases are not possible since once release must reference
// another, we will temporarily add this back.
// https://github.com/google/googletest/blob/v1.14.x/googletest/include/gtest/gtest-printers.h#L119
//
// This file can be deleted after the next Abseil and GoogleTest release.
//
// https://github.com/google/googletest/pull/4368#issuecomment-1717699895
// https://github.com/google/googletest/pull/4368#issuecomment-1717699895
using ::absl::HasAbslStringify;
} // namespace strings_internal
ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_STRINGS_INTERNAL_HAS_ABSL_STRINGIFY_H_

View File

@@ -31,16 +31,23 @@
#ifndef ABSL_STRINGS_INTERNAL_STR_JOIN_INTERNAL_H_
#define ABSL_STRINGS_INTERNAL_STR_JOIN_INTERNAL_H_
#include <cstdint>
#include <cstring>
#include <initializer_list>
#include <iterator>
#include <limits>
#include <memory>
#include <string>
#include <tuple>
#include <type_traits>
#include <utility>
#include "absl/base/config.h"
#include "absl/base/internal/raw_logging.h"
#include "absl/strings/internal/ostringstream.h"
#include "absl/strings/internal/resize_uninitialized.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
@@ -230,14 +237,19 @@ std::string JoinAlgorithm(Iterator start, Iterator end, absl::string_view s,
if (start != end) {
// Sums size
auto&& start_value = *start;
size_t result_size = start_value.size();
// Use uint64_t to prevent size_t overflow. We assume it is not possible for
// in memory strings to overflow a uint64_t.
uint64_t result_size = start_value.size();
for (Iterator it = start; ++it != end;) {
result_size += s.size();
result_size += (*it).size();
}
if (result_size > 0) {
STLStringResizeUninitialized(&result, result_size);
constexpr uint64_t kMaxSize =
uint64_t{(std::numeric_limits<size_t>::max)()};
ABSL_INTERNAL_CHECK(result_size <= kMaxSize, "size_t overflow");
STLStringResizeUninitialized(&result, static_cast<size_t>(result_size));
// Joins strings
char* result_buf = &*result.begin();
@@ -310,6 +322,15 @@ std::string JoinRange(const Range& range, absl::string_view separator) {
return JoinRange(begin(range), end(range), separator);
}
template <typename Tuple, std::size_t... I>
std::string JoinTuple(const Tuple& value, absl::string_view separator,
std::index_sequence<I...>) {
return JoinRange(
std::initializer_list<absl::string_view>{
static_cast<const AlphaNum&>(std::get<I>(value)).Piece()...},
separator);
}
} // namespace strings_internal
ABSL_NAMESPACE_END
} // namespace absl

View File

@@ -30,6 +30,7 @@
#define ABSL_STRINGS_INTERNAL_STR_SPLIT_INTERNAL_H_
#include <array>
#include <cstddef>
#include <initializer_list>
#include <iterator>
#include <tuple>
@@ -402,7 +403,10 @@ class Splitter {
ar[index].size = it->size();
++it;
} while (++index != ar.size() && !it.at_end());
v.insert(v.end(), ar.begin(), ar.begin() + index);
// We static_cast index to a signed type to work around overzealous
// compiler warnings about signedness.
v.insert(v.end(), ar.begin(),
ar.begin() + static_cast<ptrdiff_t>(index));
}
return v;
}