blob: 120afb44f9220fb0fbdeef756267889fc7e4c6b7 [file]
// Copyright 2026 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "quiche/common/quiche_cord_utils.h"
#include <array>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "absl/strings/cord.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "absl/types/span.h"
#include "quiche/common/platform/api/quiche_test.h"
#include "quiche/common/quiche_mem_slice.h"
namespace quiche::test {
namespace {
TEST(QuicheCordUtilsTest, MemSliceToCord) {
QuicheMemSlice slice1 = QuicheMemSlice::Copy("foo");
QuicheMemSlice slice2 = QuicheMemSlice::Copy("bar");
absl::Cord cord = MemSliceToCord(std::move(slice1));
cord.Append(MemSliceToCord(std::move(slice2)));
EXPECT_EQ(cord, "foobar");
}
TEST(QuicheCordUtilsTest, MemSliceToCordEmpty) {
absl::Cord cord = MemSliceToCord(QuicheMemSlice());
EXPECT_TRUE(cord.empty());
}
TEST(QuicheCordUtilsTest, MemSliceToCordNullDeleter) {
absl::string_view kText = "test";
absl::Cord cord =
MemSliceToCord(QuicheMemSlice(kText.data(), kText.size(), nullptr));
EXPECT_EQ(cord, kText);
}
TEST(QuicheCordUtilsTest, MemSliceSpanToCord) {
std::array<QuicheMemSlice, 2> slices = {QuicheMemSlice::Copy("foo"),
QuicheMemSlice::Copy("bar")};
absl::Cord cord = MemSliceSpanToCord(absl::MakeSpan(slices));
EXPECT_EQ(cord, "foobar");
}
TEST(QuicheCordUtilsTest, CordToMemSlicesInlined) {
absl::Cord cord("test");
std::vector<QuicheMemSlice> slices;
CordToMemSlicesTo(cord, slices);
ASSERT_EQ(slices.size(), 1);
EXPECT_EQ(slices[0].AsStringView(), "test");
}
TEST(QuicheCordUtilsTest, CordToMemSlicesNotInlined) {
constexpr size_t kSize = 8192;
auto buffer = std::make_unique<char[]>(kSize);
uintptr_t original_address = reinterpret_cast<uintptr_t>(buffer.get());
memset(buffer.get(), 'a', kSize);
absl::Cord cord = absl::MakeCordFromExternal(
absl::string_view(buffer.release(), kSize),
[](absl::string_view data) { delete[] data.data(); });
std::vector<QuicheMemSlice> slices;
CordToMemSlicesTo(cord, slices);
cord.Clear();
ASSERT_EQ(slices.size(), 1);
EXPECT_EQ(slices[0].length(), kSize);
EXPECT_EQ(reinterpret_cast<uintptr_t>(slices[0].data()), original_address);
EXPECT_EQ(slices[0].AsStringView(), std::string(kSize, 'a'));
}
TEST(QuicheCordUtilsTest, CordToMemSlicesLarge) {
const std::string kBlock(1024, 'a');
constexpr size_t kBlockCount = 128;
absl::Cord cord;
for (size_t i = 0; i < kBlockCount; ++i) {
cord.Append(kBlock);
}
std::vector<QuicheMemSlice> slices;
CordToMemSlicesTo(cord, slices);
cord.Clear();
ASSERT_GT(slices.size(), 1);
size_t total_size = 0;
for (const QuicheMemSlice& slice : slices) {
total_size += slice.length();
}
EXPECT_EQ(total_size, kBlock.size() * kBlockCount);
for (const QuicheMemSlice& slice : slices) {
ASSERT_THAT(slice.AsStringView(), testing::Each('a'));
}
}
TEST(QuicheCordUtilsTest, CordWithMixedTypes) {
absl::Cord cord("bar");
cord.Prepend("foo");
auto block_before = std::make_unique<std::string>(8192, 'a');
absl::string_view block_before_view(*block_before);
cord.Prepend(absl::MakeCordFromExternal(
block_before_view,
[ptr = block_before.release()](absl::string_view) { delete ptr; }));
auto block_after = std::make_unique<std::string>(8192, 'b');
absl::string_view block_after_view(*block_after);
cord.Append(absl::MakeCordFromExternal(
block_after_view,
[ptr = block_after.release()](absl::string_view) { delete ptr; }));
std::vector<QuicheMemSlice> slices;
CordToMemSlicesTo(cord, slices);
cord.Clear();
ASSERT_GT(slices.size(), 1);
std::string concatenated;
for (const QuicheMemSlice& slice : slices) {
absl::StrAppend(&concatenated, slice.AsStringView());
}
EXPECT_EQ(concatenated, absl::StrCat(std::string(8192, 'a'), "foobar",
std::string(8192, 'b')));
}
TEST(QuicheCordUtilsTest, Subcord) {
constexpr size_t kCount = 100;
absl::Cord cord;
for (size_t i = 0; i < kCount; ++i) {
cord.Append("foobar");
}
absl::Cord subcord = cord.Subcord(99, 6);
std::vector<QuicheMemSlice> slices;
CordToMemSlicesTo(subcord, slices);
ASSERT_EQ(slices.size(), 1);
EXPECT_EQ(slices[0].AsStringView(), "barfoo"); // 99 = 16 * 6 + 3
cord.Clear();
subcord.Clear();
EXPECT_EQ(slices[0].AsStringView(), "barfoo");
}
} // namespace
} // namespace quiche::test