aboutsummaryrefslogtreecommitdiff
path: root/absl/strings/internal
diff options
context:
space:
mode:
Diffstat (limited to 'absl/strings/internal')
-rw-r--r--absl/strings/internal/charconv_bigint.h10
-rw-r--r--absl/strings/internal/cord_internal.h38
-rw-r--r--absl/strings/internal/cord_rep_btree.h4
-rw-r--r--absl/strings/internal/cordz_functions.cc32
-rw-r--r--absl/strings/internal/cordz_functions.h36
-rw-r--r--absl/strings/internal/cordz_functions_test.cc14
-rw-r--r--absl/strings/internal/cordz_handle.cc48
-rw-r--r--absl/strings/internal/cordz_info.cc19
-rw-r--r--absl/strings/internal/cordz_info.h13
-rw-r--r--absl/strings/internal/cordz_info_statistics_test.cc4
-rw-r--r--absl/strings/internal/cordz_info_test.cc32
-rw-r--r--absl/strings/internal/cordz_sample_token_test.cc14
-rw-r--r--absl/strings/internal/cordz_update_scope_test.cc2
-rw-r--r--absl/strings/internal/escaping.cc7
-rw-r--r--absl/strings/internal/has_absl_stringify.h44
-rw-r--r--absl/strings/internal/str_format/convert_test.cc74
-rw-r--r--absl/strings/internal/str_join_internal.h25
-rw-r--r--absl/strings/internal/str_split_internal.h6
18 files changed, 218 insertions, 204 deletions
diff --git a/absl/strings/internal/charconv_bigint.h b/absl/strings/internal/charconv_bigint.h
index 5c0c375d..cb297676 100644
--- a/absl/strings/internal/charconv_bigint.h
+++ b/absl/strings/internal/charconv_bigint.h
@@ -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) |
diff --git a/absl/strings/internal/cord_internal.h b/absl/strings/internal/cord_internal.h
index 8744540e..f0060f10 100644
--- a/absl/strings/internal/cord_internal.h
+++ b/absl/strings/internal/cord_internal.h
@@ -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
diff --git a/absl/strings/internal/cord_rep_btree.h b/absl/strings/internal/cord_rep_btree.h
index be94b62e..ab259afe 100644
--- a/absl/strings/internal/cord_rep_btree.h
+++ b/absl/strings/internal/cord_rep_btree.h
@@ -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 {
diff --git a/absl/strings/internal/cordz_functions.cc b/absl/strings/internal/cordz_functions.cc
index 20d314f0..6033d046 100644
--- a/absl/strings/internal/cordz_functions.cc
+++ b/absl/strings/internal/cordz_functions.cc
@@ -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
diff --git a/absl/strings/internal/cordz_functions.h b/absl/strings/internal/cordz_functions.h
index ed108bf1..84c185e4 100644
--- a/absl/strings/internal/cordz_functions.h
+++ b/absl/strings/internal/cordz_functions.h
@@ -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;
-
-// 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();
-
-// 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;
+ABSL_CONST_INIT extern thread_local SamplingState cordz_next_sample;
+
+// 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);
+
+// 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
diff --git a/absl/strings/internal/cordz_functions_test.cc b/absl/strings/internal/cordz_functions_test.cc
index b70a685e..8fb93d53 100644
--- a/absl/strings/internal/cordz_functions_test.cc
+++ b/absl/strings/internal/cordz_functions_test.cc
@@ -47,9 +47,9 @@ TEST(CordzFunctionsTest, ShouldProfileDisable) {
set_cordz_mean_interval(0);
cordz_set_next_sample_for_testing(0);
- EXPECT_FALSE(cordz_should_profile());
+ EXPECT_EQ(cordz_should_profile(), 0);
// 1 << 16 is from kIntervalIfDisabled in cordz_functions.cc.
- EXPECT_THAT(cordz_next_sample, Eq(1 << 16));
+ EXPECT_THAT(cordz_next_sample.next_sample, Eq(1 << 16));
set_cordz_mean_interval(orig_sample_rate);
}
@@ -59,8 +59,8 @@ TEST(CordzFunctionsTest, ShouldProfileAlways) {
set_cordz_mean_interval(1);
cordz_set_next_sample_for_testing(1);
- EXPECT_TRUE(cordz_should_profile());
- EXPECT_THAT(cordz_next_sample, Le(1));
+ EXPECT_GT(cordz_should_profile(), 0);
+ EXPECT_THAT(cordz_next_sample.next_sample, Le(1));
set_cordz_mean_interval(orig_sample_rate);
}
@@ -74,9 +74,7 @@ TEST(CordzFunctionsTest, DoesNotAlwaysSampleFirstCord) {
do {
++tries;
ASSERT_THAT(tries, Le(1000));
- std::thread thread([&sampled] {
- sampled = cordz_should_profile();
- });
+ std::thread thread([&sampled] { sampled = cordz_should_profile() > 0; });
thread.join();
} while (sampled);
}
@@ -94,7 +92,7 @@ TEST(CordzFunctionsTest, ShouldProfileRate) {
// new value for next_sample each iteration.
cordz_set_next_sample_for_testing(0);
cordz_should_profile();
- sum_of_intervals += cordz_next_sample;
+ sum_of_intervals += cordz_next_sample.next_sample;
}
// The sum of independent exponential variables is an Erlang distribution,
diff --git a/absl/strings/internal/cordz_handle.cc b/absl/strings/internal/cordz_handle.cc
index a7061dbe..53d5f529 100644
--- a/absl/strings/internal/cordz_handle.cc
+++ b/absl/strings/internal/cordz_handle.cc
@@ -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);
diff --git a/absl/strings/internal/cordz_info.cc b/absl/strings/internal/cordz_info.cc
index b24c3da7..b7c7fed9 100644
--- a/absl/strings/internal/cordz_info.cc
+++ b/absl/strings/internal/cordz_info.cc
@@ -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.
diff --git a/absl/strings/internal/cordz_info.h b/absl/strings/internal/cordz_info.h
index 17eaa91c..2dc9d16d 100644
--- a/absl/strings/internal/cordz_info.h
+++ b/absl/strings/internal/cordz_info.h
@@ -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);
}
}
diff --git a/absl/strings/internal/cordz_info_statistics_test.cc b/absl/strings/internal/cordz_info_statistics_test.cc
index d55773f2..3e6a8a09 100644
--- a/absl/strings/internal/cordz_info_statistics_test.cc
+++ b/absl/strings/internal/cordz_info_statistics_test.cc
@@ -152,7 +152,7 @@ size_t FairShare(CordRep* rep, size_t ref = 1) {
// Samples the cord and returns CordzInfo::GetStatistics()
CordzStatistics SampleCord(CordRep* rep) {
InlineData cord(rep);
- CordzInfo::TrackCord(cord, CordzUpdateTracker::kUnknown);
+ CordzInfo::TrackCord(cord, CordzUpdateTracker::kUnknown, 1);
CordzStatistics stats = cord.cordz_info()->GetCordzStatistics();
cord.cordz_info()->Untrack();
return stats;
@@ -480,7 +480,7 @@ TEST(CordzInfoStatisticsTest, ThreadSafety) {
// 50/50 sample
if (coin_toss(gen) != 0) {
- CordzInfo::TrackCord(cord, CordzUpdateTracker::kUnknown);
+ CordzInfo::TrackCord(cord, CordzUpdateTracker::kUnknown, 1);
}
}
}
diff --git a/absl/strings/internal/cordz_info_test.cc b/absl/strings/internal/cordz_info_test.cc
index cd226c3e..81ecce2c 100644
--- a/absl/strings/internal/cordz_info_test.cc
+++ b/absl/strings/internal/cordz_info_test.cc
@@ -65,7 +65,7 @@ std::string FormatStack(absl::Span<void* const> raw_stack) {
TEST(CordzInfoTest, TrackCord) {
TestCordData data;
- CordzInfo::TrackCord(data.data, kTrackCordMethod);
+ CordzInfo::TrackCord(data.data, kTrackCordMethod, 1);
CordzInfo* info = data.data.cordz_info();
ASSERT_THAT(info, Ne(nullptr));
EXPECT_FALSE(info->is_snapshot());
@@ -91,7 +91,7 @@ TEST(CordzInfoTest, MaybeTrackChildCordWithSampling) {
TEST(CordzInfoTest, MaybeTrackChildCordWithoutSamplingParentSampled) {
CordzSamplingIntervalHelper sample_none(99999);
TestCordData parent, child;
- CordzInfo::TrackCord(parent.data, kTrackCordMethod);
+ CordzInfo::TrackCord(parent.data, kTrackCordMethod, 1);
CordzInfo::MaybeTrackCord(child.data, parent.data, kTrackCordMethod);
CordzInfo* parent_info = parent.data.cordz_info();
CordzInfo* child_info = child.data.cordz_info();
@@ -105,7 +105,7 @@ TEST(CordzInfoTest, MaybeTrackChildCordWithoutSamplingParentSampled) {
TEST(CordzInfoTest, MaybeTrackChildCordWithoutSamplingChildSampled) {
CordzSamplingIntervalHelper sample_none(99999);
TestCordData parent, child;
- CordzInfo::TrackCord(child.data, kTrackCordMethod);
+ CordzInfo::TrackCord(child.data, kTrackCordMethod, 1);
CordzInfo::MaybeTrackCord(child.data, parent.data, kTrackCordMethod);
EXPECT_THAT(child.data.cordz_info(), Eq(nullptr));
}
@@ -113,14 +113,14 @@ TEST(CordzInfoTest, MaybeTrackChildCordWithoutSamplingChildSampled) {
TEST(CordzInfoTest, MaybeTrackChildCordWithSamplingChildSampled) {
CordzSamplingIntervalHelper sample_all(1);
TestCordData parent, child;
- CordzInfo::TrackCord(child.data, kTrackCordMethod);
+ CordzInfo::TrackCord(child.data, kTrackCordMethod, 1);
CordzInfo::MaybeTrackCord(child.data, parent.data, kTrackCordMethod);
EXPECT_THAT(child.data.cordz_info(), Eq(nullptr));
}
TEST(CordzInfoTest, UntrackCord) {
TestCordData data;
- CordzInfo::TrackCord(data.data, kTrackCordMethod);
+ CordzInfo::TrackCord(data.data, kTrackCordMethod, 1);
CordzInfo* info = data.data.cordz_info();
info->Untrack();
@@ -129,7 +129,7 @@ TEST(CordzInfoTest, UntrackCord) {
TEST(CordzInfoTest, UntrackCordWithSnapshot) {
TestCordData data;
- CordzInfo::TrackCord(data.data, kTrackCordMethod);
+ CordzInfo::TrackCord(data.data, kTrackCordMethod, 1);
CordzInfo* info = data.data.cordz_info();
CordzSnapshot snapshot;
@@ -141,7 +141,7 @@ TEST(CordzInfoTest, UntrackCordWithSnapshot) {
TEST(CordzInfoTest, SetCordRep) {
TestCordData data;
- CordzInfo::TrackCord(data.data, kTrackCordMethod);
+ CordzInfo::TrackCord(data.data, kTrackCordMethod, 1);
CordzInfo* info = data.data.cordz_info();
TestCordRep rep;
@@ -155,7 +155,7 @@ TEST(CordzInfoTest, SetCordRep) {
TEST(CordzInfoTest, SetCordRepNullUntracksCordOnUnlock) {
TestCordData data;
- CordzInfo::TrackCord(data.data, kTrackCordMethod);
+ CordzInfo::TrackCord(data.data, kTrackCordMethod, 1);
CordzInfo* info = data.data.cordz_info();
info->Lock(CordzUpdateTracker::kAppendString);
@@ -169,7 +169,7 @@ TEST(CordzInfoTest, SetCordRepNullUntracksCordOnUnlock) {
TEST(CordzInfoTest, RefCordRep) {
TestCordData data;
- CordzInfo::TrackCord(data.data, kTrackCordMethod);
+ CordzInfo::TrackCord(data.data, kTrackCordMethod, 1);
CordzInfo* info = data.data.cordz_info();
size_t refcount = data.rep.rep->refcount.Get();
@@ -183,7 +183,7 @@ TEST(CordzInfoTest, RefCordRep) {
TEST(CordzInfoTest, SetCordRepRequiresMutex) {
TestCordData data;
- CordzInfo::TrackCord(data.data, kTrackCordMethod);
+ CordzInfo::TrackCord(data.data, kTrackCordMethod, 1);
CordzInfo* info = data.data.cordz_info();
TestCordRep rep;
EXPECT_DEBUG_DEATH(info->SetCordRep(rep.rep), ".*");
@@ -197,13 +197,13 @@ TEST(CordzInfoTest, TrackUntrackHeadFirstV2) {
EXPECT_THAT(CordzInfo::Head(snapshot), Eq(nullptr));
TestCordData data;
- CordzInfo::TrackCord(data.data, kTrackCordMethod);
+ CordzInfo::TrackCord(data.data, kTrackCordMethod, 1);
CordzInfo* info1 = data.data.cordz_info();
ASSERT_THAT(CordzInfo::Head(snapshot), Eq(info1));
EXPECT_THAT(info1->Next(snapshot), Eq(nullptr));
TestCordData data2;
- CordzInfo::TrackCord(data2.data, kTrackCordMethod);
+ CordzInfo::TrackCord(data2.data, kTrackCordMethod, 1);
CordzInfo* info2 = data2.data.cordz_info();
ASSERT_THAT(CordzInfo::Head(snapshot), Eq(info2));
EXPECT_THAT(info2->Next(snapshot), Eq(info1));
@@ -222,13 +222,13 @@ TEST(CordzInfoTest, TrackUntrackTailFirstV2) {
EXPECT_THAT(CordzInfo::Head(snapshot), Eq(nullptr));
TestCordData data;
- CordzInfo::TrackCord(data.data, kTrackCordMethod);
+ CordzInfo::TrackCord(data.data, kTrackCordMethod, 1);
CordzInfo* info1 = data.data.cordz_info();
ASSERT_THAT(CordzInfo::Head(snapshot), Eq(info1));
EXPECT_THAT(info1->Next(snapshot), Eq(nullptr));
TestCordData data2;
- CordzInfo::TrackCord(data2.data, kTrackCordMethod);
+ CordzInfo::TrackCord(data2.data, kTrackCordMethod, 1);
CordzInfo* info2 = data2.data.cordz_info();
ASSERT_THAT(CordzInfo::Head(snapshot), Eq(info2));
EXPECT_THAT(info2->Next(snapshot), Eq(info1));
@@ -254,7 +254,7 @@ TEST(CordzInfoTest, StackV2) {
// makes small modifications to its testing stack. 50 is sufficient to prove
// that we got a decent stack.
static constexpr int kMaxStackDepth = 50;
- CordzInfo::TrackCord(data.data, kTrackCordMethod);
+ CordzInfo::TrackCord(data.data, kTrackCordMethod, 1);
CordzInfo* info = data.data.cordz_info();
std::vector<void*> local_stack;
local_stack.resize(kMaxStackDepth);
@@ -284,7 +284,7 @@ CordzInfo* TrackChildCord(InlineData& data, const InlineData& parent) {
return data.cordz_info();
}
CordzInfo* TrackParentCord(InlineData& data) {
- CordzInfo::TrackCord(data, kTrackCordMethod);
+ CordzInfo::TrackCord(data, kTrackCordMethod, 1);
return data.cordz_info();
}
diff --git a/absl/strings/internal/cordz_sample_token_test.cc b/absl/strings/internal/cordz_sample_token_test.cc
index 6be1770d..7152603d 100644
--- a/absl/strings/internal/cordz_sample_token_test.cc
+++ b/absl/strings/internal/cordz_sample_token_test.cc
@@ -81,11 +81,11 @@ TEST(CordzSampleTokenTest, IteratorEmpty) {
TEST(CordzSampleTokenTest, Iterator) {
TestCordData cord1, cord2, cord3;
- CordzInfo::TrackCord(cord1.data, kTrackCordMethod);
+ CordzInfo::TrackCord(cord1.data, kTrackCordMethod, 1);
CordzInfo* info1 = cord1.data.cordz_info();
- CordzInfo::TrackCord(cord2.data, kTrackCordMethod);
+ CordzInfo::TrackCord(cord2.data, kTrackCordMethod, 1);
CordzInfo* info2 = cord2.data.cordz_info();
- CordzInfo::TrackCord(cord3.data, kTrackCordMethod);
+ CordzInfo::TrackCord(cord3.data, kTrackCordMethod, 1);
CordzInfo* info3 = cord3.data.cordz_info();
CordzSampleToken token;
@@ -105,21 +105,21 @@ TEST(CordzSampleTokenTest, IteratorEquality) {
TestCordData cord1;
TestCordData cord2;
TestCordData cord3;
- CordzInfo::TrackCord(cord1.data, kTrackCordMethod);
+ CordzInfo::TrackCord(cord1.data, kTrackCordMethod, 1);
CordzInfo* info1 = cord1.data.cordz_info();
CordzSampleToken token1;
// lhs starts with the CordzInfo corresponding to cord1 at the head.
CordzSampleToken::Iterator lhs = token1.begin();
- CordzInfo::TrackCord(cord2.data, kTrackCordMethod);
+ CordzInfo::TrackCord(cord2.data, kTrackCordMethod, 1);
CordzInfo* info2 = cord2.data.cordz_info();
CordzSampleToken token2;
// rhs starts with the CordzInfo corresponding to cord2 at the head.
CordzSampleToken::Iterator rhs = token2.begin();
- CordzInfo::TrackCord(cord3.data, kTrackCordMethod);
+ CordzInfo::TrackCord(cord3.data, kTrackCordMethod, 1);
CordzInfo* info3 = cord3.data.cordz_info();
// lhs is on cord1 while rhs is on cord2.
@@ -170,7 +170,7 @@ TEST(CordzSampleTokenTest, MultiThreaded) {
cord.data.clear_cordz_info();
} else {
// 2) Track
- CordzInfo::TrackCord(cord.data, kTrackCordMethod);
+ CordzInfo::TrackCord(cord.data, kTrackCordMethod, 1);
}
} else {
std::unique_ptr<CordzSampleToken>& token = tokens[index];
diff --git a/absl/strings/internal/cordz_update_scope_test.cc b/absl/strings/internal/cordz_update_scope_test.cc
index 3d08c622..1b4701f1 100644
--- a/absl/strings/internal/cordz_update_scope_test.cc
+++ b/absl/strings/internal/cordz_update_scope_test.cc
@@ -37,7 +37,7 @@ TEST(CordzUpdateScopeTest, ScopeNullptr) {
TEST(CordzUpdateScopeTest, ScopeSampledCord) {
TestCordData cord;
- CordzInfo::TrackCord(cord.data, kTrackCordMethod);
+ CordzInfo::TrackCord(cord.data, kTrackCordMethod, 1);
CordzUpdateScope scope(cord.data.cordz_info(), kTrackCordMethod);
cord.data.cordz_info()->SetCordRep(nullptr);
}
diff --git a/absl/strings/internal/escaping.cc b/absl/strings/internal/escaping.cc
index 56a4cbed..d2abe669 100644
--- a/absl/strings/internal/escaping.cc
+++ b/absl/strings/internal/escaping.cc
@@ -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;
}
diff --git a/absl/strings/internal/has_absl_stringify.h b/absl/strings/internal/has_absl_stringify.h
deleted file mode 100644
index f82cfe26..00000000
--- a/absl/strings/internal/has_absl_stringify.h
+++ /dev/null
@@ -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_
diff --git a/absl/strings/internal/str_format/convert_test.cc b/absl/strings/internal/str_format/convert_test.cc
index 7f222778..baffe052 100644
--- a/absl/strings/internal/str_format/convert_test.cc
+++ b/absl/strings/internal/str_format/convert_test.cc
@@ -785,8 +785,7 @@ TEST_F(FormatConvertTest, Uint128) {
}
template <typename Floating>
-void TestWithMultipleFormatsHelper(const std::vector<Floating> &floats,
- const std::set<Floating> &skip_verify) {
+void TestWithMultipleFormatsHelper(Floating tested_float) {
const NativePrintfTraits &native_traits = VerifyNativeImplementation();
// Reserve the space to ensure we don't allocate memory in the output itself.
std::string str_format_result;
@@ -817,41 +816,41 @@ void TestWithMultipleFormatsHelper(const std::vector<Floating> &floats,
continue;
}
- for (Floating d : floats) {
- if (!native_traits.hex_float_prefers_denormal_repr &&
- (f == 'a' || f == 'A') && std::fpclassify(d) == FP_SUBNORMAL) {
- continue;
- }
+ if (!native_traits.hex_float_prefers_denormal_repr &&
+ (f == 'a' || f == 'A') &&
+ std::fpclassify(tested_float) == FP_SUBNORMAL) {
+ continue;
+ }
int i = -10;
- FormatArgImpl args[2] = {FormatArgImpl(d), FormatArgImpl(i)};
+ FormatArgImpl args[2] = {FormatArgImpl(tested_float), FormatArgImpl(i)};
UntypedFormatSpecImpl format(fmt_str);
string_printf_result.clear();
- StrAppend(&string_printf_result, fmt_str.c_str(), d, i);
+ StrAppend(&string_printf_result, fmt_str.c_str(), tested_float, i);
str_format_result.clear();
{
AppendPack(&str_format_result, format, absl::MakeSpan(args));
}
+ // For values that we know won't match the standard library
+ // implementation we skip verification, but still run the algorithm to
+ // catch asserts/sanitizer bugs.
#ifdef _MSC_VER
// MSVC has a different rounding policy than us so we can't test our
// implementation against the native one there.
continue;
#elif defined(__APPLE__)
// Apple formats NaN differently (+nan) vs. (nan)
- if (std::isnan(d)) continue;
+ if (std::isnan(tested_float)) continue;
#endif
- if (string_printf_result != str_format_result &&
- skip_verify.find(d) == skip_verify.end()) {
- // We use ASSERT_EQ here because failures are usually correlated and a
- // bug would print way too many failed expectations causing the test
- // to time out.
- ASSERT_EQ(string_printf_result, str_format_result)
- << fmt_str << " " << StrPrint("%.18g", d) << " "
- << StrPrint("%a", d) << " " << StrPrint("%.50f", d);
- }
- }
+ // We use ASSERT_EQ here because failures are usually correlated and a
+ // bug would print way too many failed expectations causing the test
+ // to time out.
+ ASSERT_EQ(string_printf_result, str_format_result)
+ << fmt_str << " " << StrPrint("%.18g", tested_float) << " "
+ << StrPrint("%a", tested_float) << " "
+ << StrPrint("%.50f", tested_float);
}
}
}
@@ -904,14 +903,12 @@ TEST_F(FormatConvertTest, Float) {
});
floats.erase(std::unique(floats.begin(), floats.end()), floats.end());
- TestWithMultipleFormatsHelper(floats, {});
+ for (float f : floats) {
+ TestWithMultipleFormatsHelper(f);
+ }
}
TEST_F(FormatConvertTest, Double) {
- // For values that we know won't match the standard library implementation we
- // skip verification, but still run the algorithm to catch asserts/sanitizer
- // bugs.
- std::set<double> skip_verify;
std::vector<double> doubles = {0.0,
-0.0,
.99999999999999,
@@ -946,32 +943,9 @@ TEST_F(FormatConvertTest, Double) {
}
}
- // Workaround libc bug.
- // https://sourceware.org/bugzilla/show_bug.cgi?id=22142
- const bool gcc_bug_22142 =
- StrPrint("%f", std::numeric_limits<double>::max()) !=
- "1797693134862315708145274237317043567980705675258449965989174768031"
- "5726078002853876058955863276687817154045895351438246423432132688946"
- "4182768467546703537516986049910576551282076245490090389328944075868"
- "5084551339423045832369032229481658085593321233482747978262041447231"
- "68738177180919299881250404026184124858368.000000";
-
for (int exp = -300; exp <= 300; ++exp) {
const double all_ones_mantissa = 0x1fffffffffffff;
doubles.push_back(std::ldexp(all_ones_mantissa, exp));
- if (gcc_bug_22142) {
- skip_verify.insert(doubles.back());
- }
- }
-
- if (gcc_bug_22142) {
- using L = std::numeric_limits<double>;
- skip_verify.insert(L::max());
- skip_verify.insert(L::min()); // NOLINT
- skip_verify.insert(L::denorm_min());
- skip_verify.insert(-L::max());
- skip_verify.insert(-L::min()); // NOLINT
- skip_verify.insert(-L::denorm_min());
}
// Remove duplicates to speed up the logic below.
@@ -982,7 +956,9 @@ TEST_F(FormatConvertTest, Double) {
});
doubles.erase(std::unique(doubles.begin(), doubles.end()), doubles.end());
- TestWithMultipleFormatsHelper(doubles, skip_verify);
+ for (double d : doubles) {
+ TestWithMultipleFormatsHelper(d);
+ }
}
TEST_F(FormatConvertTest, DoubleRound) {
diff --git a/absl/strings/internal/str_join_internal.h b/absl/strings/internal/str_join_internal.h
index d97d5033..3e730c7a 100644
--- a/absl/strings/internal/str_join_internal.h
+++ b/absl/strings/internal/str_join_internal.h
@@ -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
diff --git a/absl/strings/internal/str_split_internal.h b/absl/strings/internal/str_split_internal.h
index 081ad85a..11ea96f2 100644
--- a/absl/strings/internal/str_split_internal.h
+++ b/absl/strings/internal/str_split_internal.h
@@ -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;
}