| // 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 |