Skip to content

Commit 638e17a

Browse files
derekmaurocopybara-github
authored andcommitted
Add absl::CopyCordToSpan()
PiperOrigin-RevId: 912247630 Change-Id: Ie5d2837c9b878e6094dce4d2ec7e60441a7c6bb7
1 parent d0d4c59 commit 638e17a

4 files changed

Lines changed: 75 additions & 0 deletions

File tree

absl/strings/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1014,6 +1014,7 @@ cc_test(
10141014
"//absl/log:check",
10151015
"//absl/random",
10161016
"//absl/types:compare",
1017+
"//absl/types:span",
10171018
"@googletest//:gtest",
10181019
"@googletest//:gtest_main",
10191020
],

absl/strings/cord.cc

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1086,6 +1086,24 @@ void Cord::CopyToArraySlowPath(char* absl_nonnull dst) const {
10861086
}
10871087
}
10881088

1089+
size_t CopyCordToSpan(const Cord& src, absl::Span<char> dst) {
1090+
if (src.size() <= dst.size()) {
1091+
src.CopyToArrayImpl(dst.data());
1092+
return src.size();
1093+
}
1094+
1095+
const size_t result = dst.size();
1096+
for (absl::string_view chunk : src.Chunks()) {
1097+
size_t n = std::min(chunk.size(), dst.size());
1098+
if (n == 0) {
1099+
break;
1100+
}
1101+
memcpy(dst.data(), chunk.data(), n);
1102+
dst.remove_prefix(n);
1103+
}
1104+
return result;
1105+
}
1106+
10891107
Cord Cord::ChunkIterator::AdvanceAndReadBytes(size_t n) {
10901108
// Failure of this assertion indicates an attempt to iterate past `end()`.
10911109
absl::base_internal::HardeningAssertGE(bytes_remaining_, n);

absl/strings/cord.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@
9898
#include "absl/strings/string_view.h"
9999
#include "absl/types/compare.h"
100100
#include "absl/types/optional.h"
101+
#include "absl/types/span.h"
101102

102103
namespace absl {
103104
ABSL_NAMESPACE_BEGIN
@@ -107,6 +108,7 @@ template <typename Releaser>
107108
Cord MakeCordFromExternal(absl::string_view, Releaser&&);
108109
void CopyCordToString(const Cord& src, std::string* absl_nonnull dst);
109110
void AppendCordToString(const Cord& src, std::string* absl_nonnull dst);
111+
[[nodiscard]] size_t CopyCordToSpan(const Cord& src, absl::Span<char> dst);
110112

111113
// Cord memory accounting modes
112114
enum class CordMemoryAccounting {
@@ -434,6 +436,12 @@ class Cord {
434436
friend void AppendCordToString(const Cord& src,
435437
std::string* absl_nonnull dst);
436438

439+
// CopyCordToSpan()
440+
//
441+
// Copies up to `dest.size()` bytes starting from the beginning of `src` to
442+
// `dst`. Returns the number of bytes copied.
443+
friend size_t CopyCordToSpan(const Cord& src, absl::Span<char> dst);
444+
437445
class CharIterator;
438446

439447
//----------------------------------------------------------------------------

absl/strings/cord_test.cc

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
#include "absl/strings/str_format.h"
6363
#include "absl/strings/string_view.h"
6464
#include "absl/types/compare.h"
65+
#include "absl/types/span.h"
6566

6667
// convenience local constants
6768
static constexpr auto FLAT = absl::cord_internal::FLAT;
@@ -735,6 +736,53 @@ TEST_P(CordTest, AppendToString) {
735736
"appending ", "to ", "a ", "string."})));
736737
}
737738

739+
static void VerifyCopyToSpan(const absl::Cord& cord) {
740+
// Test with span exactly the same size as the cord.
741+
{
742+
std::string dst(cord.size(), '\0');
743+
size_t copied = absl::CopyCordToSpan(cord, absl::MakeSpan(dst));
744+
EXPECT_EQ(copied, cord.size());
745+
EXPECT_EQ(dst, cord);
746+
}
747+
748+
// Test with span larger than the cord.
749+
{
750+
std::string dst(cord.size() + 10, 'x');
751+
size_t copied = absl::CopyCordToSpan(cord, absl::MakeSpan(dst));
752+
EXPECT_EQ(copied, cord.size());
753+
EXPECT_EQ(absl::string_view(dst).substr(0, copied), cord);
754+
if (cord.size() < dst.size()) {
755+
absl::string_view tail = absl::string_view(dst).substr(copied);
756+
EXPECT_EQ(tail, std::string(tail.size(), 'x'));
757+
}
758+
}
759+
760+
// Test with span smaller than the cord.
761+
{
762+
size_t target_size = cord.size() / 2;
763+
std::string dst(target_size, '\0');
764+
size_t copied = absl::CopyCordToSpan(cord, absl::MakeSpan(dst));
765+
EXPECT_EQ(copied, target_size);
766+
EXPECT_EQ(dst, std::string(cord).substr(0, target_size));
767+
}
768+
769+
// Test with empty span.
770+
{
771+
char c = 'x';
772+
size_t copied = absl::CopyCordToSpan(cord, absl::MakeSpan(&c, 0));
773+
EXPECT_EQ(copied, 0);
774+
EXPECT_EQ(c, 'x');
775+
}
776+
}
777+
778+
TEST_P(CordTest, CopyToSpan) {
779+
VerifyCopyToSpan(absl::Cord()); // Empty cords cannot be hardened.
780+
VerifyCopyToSpan(MaybeHardened(absl::Cord("small cord")));
781+
VerifyCopyToSpan(MaybeHardened(
782+
absl::MakeFragmentedCord({"fragmented ", "cord ", "to ", "test ",
783+
"copying ", "to ", "a ", "span."})));
784+
}
785+
738786
TEST_P(CordTest, AppendEmptyBuffer) {
739787
absl::Cord cord;
740788
cord.Append(absl::CordBuffer());

0 commit comments

Comments
 (0)