blob: 51d9403587f249693d59aa98c2709408a7fd3e1f [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 <cstddef>
#include <cstdint>
#include <memory>
#include <utility>
#include "absl/strings/cord.h"
#include "absl/strings/string_view.h"
#include "absl/types/span.h"
#include "quiche/common/platform/api/quiche_logging.h"
#include "quiche/common/quiche_callbacks.h"
#include "quiche/common/quiche_mem_slice.h"
namespace quiche {
namespace {
// Explicitly stated in the absl::Cord documentation.
constexpr size_t kMaxInlinedCordSize = 15;
} // namespace
absl::Cord MemSliceToCord(QuicheMemSlice slice) {
if (slice.empty()) {
return absl::Cord();
}
QuicheMemSlice::ReleasedSlice released_slice = std::move(slice).Release();
if (released_slice.callback == nullptr) {
released_slice.callback = [](absl::string_view) {};
}
return absl::MakeCordFromExternal(released_slice.data,
std::move(released_slice.callback));
}
absl::Cord MemSliceSpanToCord(absl::Span<QuicheMemSlice> slices) {
absl::Cord cord;
for (QuicheMemSlice& slice : slices) {
cord.Append(MemSliceToCord(std::move(slice)));
}
return cord;
}
void CordToMemSlices(const absl::Cord& cord,
UnretainedCallback<void(QuicheMemSlice)> sink) {
size_t current_offset = 0;
for (absl::string_view chunk : cord.Chunks()) {
// absl::Cord does not provide any API to access individual chunks or to
// extract the release callback from a chunk. To side-step this issue,
// allocate an instance of absl::Cord on the heap, and delete it from the
// QuicheMemSlice release callback. The heap allocation is necessary since
// absl::Cord supports small string inlining, meaning the resulting
// absl::string_view is not guaranteed to point to a stable heap-allocated
// address otherwise.
auto subcord = std::make_unique<absl::Cord>(
cord.Subcord(current_offset, chunk.size()));
QUICHE_DCHECK(subcord->TryFlat().has_value());
// Despite the QUICHE_DCHECK above, the production code below still uses
// `Flatten()` as a fail-safe.
absl::string_view stored_chunk = subcord->Flatten();
QUICHE_DCHECK_EQ(stored_chunk, chunk);
if (chunk.size() > kMaxInlinedCordSize) {
// absl::Cord has a documented inlining threshold; ensure that if it is
// exceeded, the data address does not change, since the goal of this API
// is to avoid copies.
QUICHE_DCHECK_EQ(reinterpret_cast<uintptr_t>(stored_chunk.data()),
reinterpret_cast<uintptr_t>(chunk.data()));
}
sink(QuicheMemSlice(
stored_chunk.data(), stored_chunk.size(),
[ptr = subcord.release()](absl::string_view) { delete ptr; }));
current_offset += chunk.size();
}
}
} // namespace quiche