summaryrefslogtreecommitdiff
path: root/absl/strings/internal
diff options
context:
space:
mode:
Diffstat (limited to 'absl/strings/internal')
-rw-r--r--absl/strings/internal/cord_internal.h20
-rw-r--r--absl/strings/internal/cord_rep_consume.cc129
-rw-r--r--absl/strings/internal/cord_rep_consume.h50
-rw-r--r--absl/strings/internal/cord_rep_consume_test.cc173
-rw-r--r--absl/strings/internal/cord_rep_flat.h10
-rw-r--r--absl/strings/internal/cord_rep_ring.cc118
-rw-r--r--absl/strings/internal/str_split_internal.h2
7 files changed, 373 insertions, 129 deletions
diff --git a/absl/strings/internal/cord_internal.h b/absl/strings/internal/cord_internal.h
index 1e2436ba..bddc9815 100644
--- a/absl/strings/internal/cord_internal.h
+++ b/absl/strings/internal/cord_internal.h
@@ -150,22 +150,24 @@ struct CordRepExternal;
struct CordRepFlat;
struct CordRepSubstring;
class CordRepRing;
+class CordRepBtree;
// Various representations that we allow
enum CordRepKind {
CONCAT = 0,
SUBSTRING = 1,
- RING = 2,
- EXTERNAL = 3,
+ BTREE = 2,
+ RING = 3,
+ EXTERNAL = 4,
// We have different tags for different sized flat arrays,
- // starting with FLAT, and limited to MAX_FLAT_TAG. The 224 value is based on
+ // starting with FLAT, and limited to MAX_FLAT_TAG. The 225 value is based on
// the current 'size to tag' encoding of 8 / 32 bytes. If a new tag is needed
// in the future, then 'FLAT' and 'MAX_FLAT_TAG' should be adjusted as well
// as the Tag <---> Size logic so that FLAT stil represents the minimum flat
// allocation size. (32 bytes as of now).
- FLAT = 4,
- MAX_FLAT_TAG = 224
+ FLAT = 5,
+ MAX_FLAT_TAG = 225
};
// There are various locations where we want to check if some rep is a 'plain'
@@ -175,8 +177,9 @@ enum CordRepKind {
// so likewise align RING to EXTERNAL.
// Note that we can leave this optimization to the compiler. The compiler will
// DTRT when it sees a condition like `tag == EXTERNAL || tag >= FLAT`.
-static_assert(EXTERNAL == RING + 1, "RING and EXTERNAL values not consecutive");
-static_assert(FLAT == EXTERNAL + 1, "EXTERNAL and FLAT values not consecutive");
+static_assert(RING == BTREE + 1, "BTREE and RING not consecutive");
+static_assert(EXTERNAL == RING + 1, "BTREE and EXTERNAL not consecutive");
+static_assert(FLAT == EXTERNAL + 1, "EXTERNAL and FLAT not consecutive");
struct CordRep {
CordRep() = default;
@@ -203,6 +206,9 @@ struct CordRep {
inline CordRepFlat* flat();
inline const CordRepFlat* flat() const;
+ inline CordRepBtree* btree();
+ inline const CordRepBtree* btree() const;
+
// --------------------------------------------------------------------
// Memory management
diff --git a/absl/strings/internal/cord_rep_consume.cc b/absl/strings/internal/cord_rep_consume.cc
new file mode 100644
index 00000000..81514543
--- /dev/null
+++ b/absl/strings/internal/cord_rep_consume.cc
@@ -0,0 +1,129 @@
+// Copyright 2021 The Abseil Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "absl/strings/internal/cord_rep_consume.h"
+
+#include <array>
+#include <utility>
+
+#include "absl/container/inlined_vector.h"
+#include "absl/functional/function_ref.h"
+#include "absl/strings/internal/cord_internal.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace cord_internal {
+
+namespace {
+
+// Unrefs the provided `substring`, and returns `substring->child`
+// Adds or assumes a reference on `substring->child`
+CordRep* ClipSubstring(CordRepSubstring* substring) {
+ CordRep* child = substring->child;
+ if (substring->refcount.IsOne()) {
+ delete substring;
+ } else {
+ CordRep::Ref(child);
+ CordRep::Unref(substring);
+ }
+ return child;
+}
+
+// Unrefs the provided `concat`, and returns `{concat->left, concat->right}`
+// Adds or assumes a reference on `concat->left` and `concat->right`.
+// Returns an array of 2 elements containing the left and right nodes.
+std::array<CordRep*, 2> ClipConcat(CordRepConcat* concat) {
+ std::array<CordRep*, 2> result{concat->left, concat->right};
+ if (concat->refcount.IsOne()) {
+ delete concat;
+ } else {
+ CordRep::Ref(result[0]);
+ CordRep::Ref(result[1]);
+ CordRep::Unref(concat);
+ }
+ return result;
+}
+
+void Consume(bool forward, CordRep* rep, ConsumeFn consume_fn) {
+ size_t offset = 0;
+ size_t length = rep->length;
+ struct Entry {
+ CordRep* rep;
+ size_t offset;
+ size_t length;
+ };
+ absl::InlinedVector<Entry, 40> stack;
+
+ for (;;) {
+ if (rep->tag == CONCAT) {
+ std::array<CordRep*, 2> res = ClipConcat(rep->concat());
+ CordRep* left = res[0];
+ CordRep* right = res[1];
+
+ if (left->length <= offset) {
+ // Don't need left node
+ offset -= left->length;
+ CordRep::Unref(left);
+ rep = right;
+ continue;
+ }
+
+ size_t length_left = left->length - offset;
+ if (length_left >= length) {
+ // Don't need right node
+ CordRep::Unref(right);
+ rep = left;
+ continue;
+ }
+
+ // Need both nodes
+ size_t length_right = length - length_left;
+ if (forward) {
+ stack.push_back({right, 0, length_right});
+ rep = left;
+ length = length_left;
+ } else {
+ stack.push_back({left, offset, length_left});
+ rep = right;
+ offset = 0;
+ length = length_right;
+ }
+ } else if (rep->tag == SUBSTRING) {
+ offset += rep->substring()->start;
+ rep = ClipSubstring(rep->substring());
+ } else {
+ consume_fn(rep, offset, length);
+ if (stack.empty()) return;
+
+ rep = stack.back().rep;
+ offset = stack.back().offset;
+ length = stack.back().length;
+ stack.pop_back();
+ }
+ }
+}
+
+} // namespace
+
+void Consume(CordRep* rep, ConsumeFn consume_fn) {
+ return Consume(true, rep, std::move(consume_fn));
+}
+
+void ReverseConsume(CordRep* rep, ConsumeFn consume_fn) {
+ return Consume(false, rep, std::move(consume_fn));
+}
+
+} // namespace cord_internal
+ABSL_NAMESPACE_END
+} // namespace absl
diff --git a/absl/strings/internal/cord_rep_consume.h b/absl/strings/internal/cord_rep_consume.h
new file mode 100644
index 00000000..d46fca2b
--- /dev/null
+++ b/absl/strings/internal/cord_rep_consume.h
@@ -0,0 +1,50 @@
+// Copyright 2021 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_CORD_REP_CONSUME_H_
+#define ABSL_STRINGS_INTERNAL_CORD_REP_CONSUME_H_
+
+#include <functional>
+
+#include "absl/functional/function_ref.h"
+#include "absl/strings/internal/cord_internal.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace cord_internal {
+
+// Functor for the Consume() and ReverseConsume() functions:
+// void ConsumeFunc(CordRep* rep, size_t offset, size_t length);
+// See the Consume() and ReverseConsume() function comments for documentation.
+using ConsumeFn = FunctionRef<void(CordRep*, size_t, size_t)>;
+
+// Consume() and ReverseConsume() consume CONCAT based trees and invoke the
+// provided functor with the contained nodes in the proper forward or reverse
+// order, which is used to convert CONCAT trees into other tree or cord data.
+// All CONCAT and SUBSTRING nodes are processed internally. The 'offset`
+// parameter of the functor is non-zero for any nodes below SUBSTRING nodes.
+// It's up to the caller to form these back into SUBSTRING nodes or otherwise
+// store offset / prefix information. These functions are intended to be used
+// only for migration / transitional code where due to factors such as ODR
+// violations, we can not 100% guarantee that all code respects 'new format'
+// settings and flags, so we need to be able to parse old data on the fly until
+// all old code is deprecated / no longer the default format.
+void Consume(CordRep* rep, ConsumeFn consume_fn);
+void ReverseConsume(CordRep* rep, ConsumeFn consume_fn);
+
+} // namespace cord_internal
+ABSL_NAMESPACE_END
+} // namespace absl
+
+#endif // ABSL_STRINGS_INTERNAL_CORD_REP_CONSUME_H_
diff --git a/absl/strings/internal/cord_rep_consume_test.cc b/absl/strings/internal/cord_rep_consume_test.cc
new file mode 100644
index 00000000..e507824b
--- /dev/null
+++ b/absl/strings/internal/cord_rep_consume_test.cc
@@ -0,0 +1,173 @@
+// Copyright 2021 The Abseil Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "absl/strings/internal/cord_rep_consume.h"
+
+#include <functional>
+#include <utility>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "absl/strings/internal/cord_internal.h"
+#include "absl/strings/internal/cord_rep_flat.h"
+
+namespace absl {
+ABSL_NAMESPACE_BEGIN
+namespace cord_internal {
+namespace {
+
+using testing::InSequence;
+using testing::MockFunction;
+
+// Returns the depth of a node
+int Depth(const CordRep* rep) {
+ return (rep->tag == CONCAT) ? rep->concat()->depth() : 0;
+}
+
+// Creates a concatenation of the specified nodes.
+CordRepConcat* CreateConcat(CordRep* left, CordRep* right) {
+ auto* concat = new CordRepConcat();
+ concat->tag = CONCAT;
+ concat->left = left;
+ concat->right = right;
+ concat->length = left->length + right->length;
+ concat->set_depth(1 + (std::max)(Depth(left), Depth(right)));
+ return concat;
+}
+
+// Creates a flat with the length set to `length`
+CordRepFlat* CreateFlatWithLength(size_t length) {
+ auto* flat = CordRepFlat::New(length);
+ flat->length = length;
+ return flat;
+}
+
+// Creates a substring node on the specified child.
+CordRepSubstring* CreateSubstring(CordRep* child, size_t start, size_t length) {
+ auto* rep = new CordRepSubstring();
+ rep->length = length;
+ rep->tag = SUBSTRING;
+ rep->start = start;
+ rep->child = child;
+ return rep;
+}
+
+// Flats we use in the tests
+CordRep* flat[6];
+
+// Creates a test tree
+CordRep* CreateTestTree() {
+ flat[0] = CreateFlatWithLength(1);
+ flat[1] = CreateFlatWithLength(7);
+ CordRepConcat* left = CreateConcat(flat[0], CreateSubstring(flat[1], 2, 4));
+
+ flat[2] = CreateFlatWithLength(9);
+ flat[3] = CreateFlatWithLength(13);
+ CordRepConcat* right1 = CreateConcat(flat[2], flat[3]);
+
+ flat[4] = CreateFlatWithLength(15);
+ flat[5] = CreateFlatWithLength(19);
+ CordRepConcat* right2 = CreateConcat(flat[4], flat[5]);
+
+ CordRepConcat* right = CreateConcat(right1, CreateSubstring(right2, 5, 17));
+ return CreateConcat(left, right);
+}
+
+TEST(CordRepConsumeTest, Consume) {
+ InSequence in_sequence;
+ CordRep* tree = CreateTestTree();
+ MockFunction<void(CordRep*, size_t, size_t)> consume;
+ EXPECT_CALL(consume, Call(flat[0], 0, 1));
+ EXPECT_CALL(consume, Call(flat[1], 2, 4));
+ EXPECT_CALL(consume, Call(flat[2], 0, 9));
+ EXPECT_CALL(consume, Call(flat[3], 0, 13));
+ EXPECT_CALL(consume, Call(flat[4], 5, 10));
+ EXPECT_CALL(consume, Call(flat[5], 0, 7));
+ Consume(tree, consume.AsStdFunction());
+ for (CordRep* rep : flat) {
+ EXPECT_TRUE(rep->refcount.IsOne());
+ CordRep::Unref(rep);
+ }
+}
+
+TEST(CordRepConsumeTest, ConsumeShared) {
+ InSequence in_sequence;
+ CordRep* tree = CreateTestTree();
+ MockFunction<void(CordRep*, size_t, size_t)> consume;
+ EXPECT_CALL(consume, Call(flat[0], 0, 1));
+ EXPECT_CALL(consume, Call(flat[1], 2, 4));
+ EXPECT_CALL(consume, Call(flat[2], 0, 9));
+ EXPECT_CALL(consume, Call(flat[3], 0, 13));
+ EXPECT_CALL(consume, Call(flat[4], 5, 10));
+ EXPECT_CALL(consume, Call(flat[5], 0, 7));
+ Consume(CordRep::Ref(tree), consume.AsStdFunction());
+ for (CordRep* rep : flat) {
+ EXPECT_FALSE(rep->refcount.IsOne());
+ CordRep::Unref(rep);
+ }
+ CordRep::Unref(tree);
+}
+
+TEST(CordRepConsumeTest, Reverse) {
+ InSequence in_sequence;
+ CordRep* tree = CreateTestTree();
+ MockFunction<void(CordRep*, size_t, size_t)> consume;
+ EXPECT_CALL(consume, Call(flat[5], 0, 7));
+ EXPECT_CALL(consume, Call(flat[4], 5, 10));
+ EXPECT_CALL(consume, Call(flat[3], 0, 13));
+ EXPECT_CALL(consume, Call(flat[2], 0, 9));
+ EXPECT_CALL(consume, Call(flat[1], 2, 4));
+ EXPECT_CALL(consume, Call(flat[0], 0, 1));
+ ReverseConsume(tree, consume.AsStdFunction());
+ for (CordRep* rep : flat) {
+ EXPECT_TRUE(rep->refcount.IsOne());
+ CordRep::Unref(rep);
+ }
+}
+
+TEST(CordRepConsumeTest, ReverseShared) {
+ InSequence in_sequence;
+ CordRep* tree = CreateTestTree();
+ MockFunction<void(CordRep*, size_t, size_t)> consume;
+ EXPECT_CALL(consume, Call(flat[5], 0, 7));
+ EXPECT_CALL(consume, Call(flat[4], 5, 10));
+ EXPECT_CALL(consume, Call(flat[3], 0, 13));
+ EXPECT_CALL(consume, Call(flat[2], 0, 9));
+ EXPECT_CALL(consume, Call(flat[1], 2, 4));
+ EXPECT_CALL(consume, Call(flat[0], 0, 1));
+ ReverseConsume(CordRep::Ref(tree), consume.AsStdFunction());
+ for (CordRep* rep : flat) {
+ EXPECT_FALSE(rep->refcount.IsOne());
+ CordRep::Unref(rep);
+ }
+ CordRep::Unref(tree);
+}
+
+TEST(CordRepConsumeTest, UnreachableFlat) {
+ InSequence in_sequence;
+ CordRepFlat* flat1 = CreateFlatWithLength(10);
+ CordRepFlat* flat2 = CreateFlatWithLength(20);
+ CordRepConcat* concat = CreateConcat(flat1, flat2);
+ CordRepSubstring* tree = CreateSubstring(concat, 15, 10);
+ MockFunction<void(CordRep*, size_t, size_t)> consume;
+ EXPECT_CALL(consume, Call(flat2, 5, 10));
+ Consume(tree, consume.AsStdFunction());
+ EXPECT_TRUE(flat2->refcount.IsOne());
+ CordRep::Unref(flat2);
+}
+
+} // namespace
+} // namespace cord_internal
+ABSL_NAMESPACE_END
+} // namespace absl
diff --git a/absl/strings/internal/cord_rep_flat.h b/absl/strings/internal/cord_rep_flat.h
index a98aa9df..cddb282f 100644
--- a/absl/strings/internal/cord_rep_flat.h
+++ b/absl/strings/internal/cord_rep_flat.h
@@ -44,11 +44,11 @@ static constexpr size_t kMaxFlatLength = kMaxFlatSize - kFlatOverhead;
static constexpr size_t kMinFlatLength = kMinFlatSize - kFlatOverhead;
constexpr uint8_t AllocatedSizeToTagUnchecked(size_t size) {
- return static_cast<uint8_t>((size <= 1024) ? size / 8
- : 128 + size / 32 - 1024 / 32);
+ return static_cast<uint8_t>((size <= 1024) ? size / 8 + 1
+ : 129 + size / 32 - 1024 / 32);
}
-static_assert(kMinFlatSize / 8 >= FLAT, "");
+static_assert(kMinFlatSize / 8 + 1 >= FLAT, "");
static_assert(AllocatedSizeToTagUnchecked(kMaxFlatSize) <= MAX_FLAT_TAG, "");
// Helper functions for rounded div, and rounding to exact sizes.
@@ -73,7 +73,7 @@ inline uint8_t AllocatedSizeToTag(size_t size) {
// Converts the provided tag to the corresponding allocated size
constexpr size_t TagToAllocatedSize(uint8_t tag) {
- return (tag <= 128) ? (tag * 8) : (1024 + (tag - 128) * 32);
+ return (tag <= 129) ? ((tag - 1) * 8) : (1024 + (tag - 129) * 32);
}
// Converts the provided tag to the corresponding available data length
@@ -82,7 +82,7 @@ constexpr size_t TagToLength(uint8_t tag) {
}
// Enforce that kMaxFlatSize maps to a well-known exact tag value.
-static_assert(TagToAllocatedSize(224) == kMaxFlatSize, "Bad tag logic");
+static_assert(TagToAllocatedSize(225) == kMaxFlatSize, "Bad tag logic");
struct CordRepFlat : public CordRep {
// Creates a new flat node.
diff --git a/absl/strings/internal/cord_rep_ring.cc b/absl/strings/internal/cord_rep_ring.cc
index f78c94e1..20a6fc2b 100644
--- a/absl/strings/internal/cord_rep_ring.cc
+++ b/absl/strings/internal/cord_rep_ring.cc
@@ -26,6 +26,7 @@
#include "absl/base/macros.h"
#include "absl/container/inlined_vector.h"
#include "absl/strings/internal/cord_internal.h"
+#include "absl/strings/internal/cord_rep_consume.h"
#include "absl/strings/internal/cord_rep_flat.h"
namespace absl {
@@ -49,14 +50,6 @@ inline void CheckCapacity(size_t n, size_t extra) {
}
}
-// Removes a reference from `rep` only.
-// Asserts that the refcount after decrement is not zero.
-inline bool UnrefNeverOne(CordRep* rep) {
- bool result = rep->refcount.Decrement();
- assert(result);
- return result;
-}
-
// Creates a flat from the provided string data, allocating up to `extra`
// capacity in the returned flat depending on kMaxFlatLength limitations.
// Requires `len` to be less or equal to `kMaxFlatLength`
@@ -68,40 +61,6 @@ CordRepFlat* CreateFlat(const char* s, size_t n, size_t extra = 0) { // NOLINT
return rep;
}
-// Unrefs the provided `substring`, and returns `substring->child`
-// Adds or assumes a reference on `substring->child`
-CordRep* ClipSubstring(CordRepSubstring* substring) {
- CordRep* child = substring->child;
- if (substring->refcount.IsOne()) {
- delete substring;
- } else {
- CordRep::Ref(child);
- if (ABSL_PREDICT_FALSE(!substring->refcount.Decrement())) {
- UnrefNeverOne(child);
- delete substring;
- }
- }
- return child;
-}
-
-// Unrefs the provided `concat`, and returns `{concat->left, concat->right}`
-// Adds or assumes a reference on `concat->left` and `concat->right`.
-std::pair<CordRep*, CordRep*> ClipConcat(CordRepConcat* concat) {
- auto result = std::make_pair(concat->left, concat->right);
- if (concat->refcount.IsOne()) {
- delete concat;
- } else {
- CordRep::Ref(result.first);
- CordRep::Ref(result.second);
- if (ABSL_PREDICT_FALSE(!concat->refcount.Decrement())) {
- UnrefNeverOne(result.first);
- UnrefNeverOne(result.second);
- delete concat;
- }
- }
- return result;
-}
-
// Unrefs the entries in `[head, tail)`.
// Requires all entries to be a FLAT or EXTERNAL node.
void UnrefEntries(const CordRepRing* rep, index_type head, index_type tail) {
@@ -117,79 +76,6 @@ void UnrefEntries(const CordRepRing* rep, index_type head, index_type tail) {
});
}
-template <typename F>
-void Consume(Direction direction, CordRep* rep, F&& fn) {
- size_t offset = 0;
- size_t length = rep->length;
- struct Entry {
- CordRep* rep;
- size_t offset;
- size_t length;
- };
- absl::InlinedVector<Entry, 40> stack;
-
- for (;;) {
- if (rep->tag >= FLAT || rep->tag == EXTERNAL || rep->tag == RING) {
- fn(rep, offset, length);
- if (stack.empty()) return;
-
- rep = stack.back().rep;
- offset = stack.back().offset;
- length = stack.back().length;
- stack.pop_back();
- } else if (rep->tag == SUBSTRING) {
- offset += rep->substring()->start;
- rep = ClipSubstring(rep->substring());
- } else if (rep->tag == CONCAT) {
- auto res = ClipConcat(rep->concat());
- CordRep* left = res.first;
- CordRep* right = res.second;
-
- if (left->length <= offset) {
- // Don't need left node
- offset -= left->length;
- CordRep::Unref(left);
- rep = right;
- continue;
- }
-
- size_t length_left = left->length - offset;
- if (length_left >= length) {
- // Don't need right node
- CordRep::Unref(right);
- rep = left;
- continue;
- }
-
- // Need both nodes
- size_t length_right = length - length_left;
- if (direction == Direction::kReversed) {
- stack.push_back({left, offset, length_left});
- rep = right;
- offset = 0;
- length = length_right;
- } else {
- stack.push_back({right, 0, length_right});
- rep = left;
- length = length_left;
- }
- } else {
- assert("Valid tag" == nullptr);
- return;
- }
- }
-}
-
-template <typename F>
-void Consume(CordRep* rep, F&& fn) {
- return Consume(Direction::kForward, rep, std::forward<F>(fn));
-}
-
-template <typename F>
-void RConsume(CordRep* rep, F&& fn) {
- return Consume(Direction::kReversed, rep, std::forward<F>(fn));
-}
-
} // namespace
std::ostream& operator<<(std::ostream& s, const CordRepRing& rep) {
@@ -581,7 +467,7 @@ CordRepRing* CordRepRing::Append(CordRepRing* rep, CordRep* child) {
}
CordRepRing* CordRepRing::PrependSlow(CordRepRing* rep, CordRep* child) {
- RConsume(child, [&](CordRep* child_arg, size_t offset, size_t len) {
+ ReverseConsume(child, [&](CordRep* child_arg, size_t offset, size_t len) {
if (IsFlatOrExternal(child_arg)) {
rep = PrependLeaf(rep, child_arg, offset, len);
} else {
diff --git a/absl/strings/internal/str_split_internal.h b/absl/strings/internal/str_split_internal.h
index 17c1bfe8..e7664216 100644
--- a/absl/strings/internal/str_split_internal.h
+++ b/absl/strings/internal/str_split_internal.h
@@ -64,7 +64,7 @@ class ConvertibleToStringView {
ConvertibleToStringView(const std::string& s) // NOLINT(runtime/explicit)
: value_(s) {}
- // Matches rvalue strings and moves their data to a member.
+ // Disable conversion from rvalue strings.
ConvertibleToStringView(std::string&& s) = delete;
ConvertibleToStringView(const std::string&& s) = delete;