1
0
mirror of https://github.com/facebookincubator/mvfst.git synced 2025-11-22 16:02:34 +03:00
Files
mvfst/quic/common/ChainedByteRange.cpp
Aman Sharma eafe595f89 Make a ChainedByteRange abstraction
Summary: See title

Reviewed By: hanidamlaj

Differential Revision: D57080101

fbshipit-source-id: 6334f7e1dfc1fc2ea7867265bc8d0f8ee6343d3b
2024-07-03 15:19:43 -07:00

260 lines
6.7 KiB
C++

/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <quic/common/ChainedByteRange.h>
namespace quic {
[[nodiscard]] bool ChainedByteRange::empty() const {
if (range_.size() != 0) {
return false;
}
for (auto* current = next_; current != this; current = current->next_) {
if (current->range_.size() != 0) {
return false;
}
}
return true;
}
[[nodiscard]] std::string ChainedByteRange::toStr() const {
std::string result;
result.reserve(computeChainDataLength());
result.append(range_.toString());
for (auto* current = next_; current != this; current = current->next_) {
result.append(current->range_.toString());
}
return result;
}
[[nodiscard]] size_t ChainedByteRange::computeChainDataLength() const {
size_t fullLength = range_.size();
for (auto* current = next_; current != this; current = current->next_) {
fullLength += current->range_.size();
}
return fullLength;
}
ChainedByteRangeHead::ChainedByteRangeHead(const Buf& buf) {
if (!buf || buf->empty()) {
return;
}
auto it = buf->begin();
while (it != buf->end() && it->empty()) {
it++;
}
CHECK(it != buf->end());
head.range_ = *it++;
chainLength_ += head.range_.size();
ChainedByteRange* cur = &head;
for (; it != buf->end(); it++) {
chainLength_ += it->size();
auto next = std::make_unique<ChainedByteRange>().release();
next->range_ = *it;
next->prev_ = cur;
cur->next_ = next;
cur = next;
}
cur->next_ = &head;
head.prev_ = cur;
}
void ChainedByteRangeHead::append(const Buf& buf) {
if (!buf || buf->empty()) {
return;
}
auto it = buf->begin();
while (it != buf->end() && it->empty()) {
it++;
}
CHECK(it != buf->end());
// We know that *it is non-empty at this point because of the initial
// check that the chain is non-empty.
if (head.range_.empty()) {
head.range_ = *it;
chainLength_ += it->size();
it++;
}
ChainedByteRange* tail = head.prev_;
while (it != buf->end()) {
if (it->empty()) {
it++;
continue;
}
auto* newElement = std::make_unique<ChainedByteRange>(*it).release();
chainLength_ += it->size();
newElement->next_ = &head;
newElement->prev_ = tail;
tail->next_ = newElement;
tail = newElement;
head.prev_ = newElement;
it++;
}
}
void ChainedByteRangeHead::append(ChainedByteRangeHead&& chainHead) {
ChainedByteRange* oldTail = head.prev_;
// Since we're merging the input chain into this one, we need to create a
// ChainedByteRange for the data that's held as the first buffer in the input
// chain.
ChainedByteRange* headSubstitute =
std::make_unique<ChainedByteRange>(chainHead.head.getRange()).release();
ChainedByteRange* newTail = (chainHead.head.prev_ == &chainHead.head)
? headSubstitute
: chainHead.head.prev_;
headSubstitute->next_ =
(newTail == &chainHead.head) ? headSubstitute : chainHead.head.next_;
chainHead.head.next_->prev_ = headSubstitute;
headSubstitute->prev_ = oldTail;
oldTail->next_ = headSubstitute;
newTail->next_ = &head;
head.prev_ = newTail;
chainLength_ += chainHead.chainLength_;
chainHead.head.next_ = chainHead.head.prev_ = &chainHead.head;
chainHead.chainLength_ = 0;
}
ChainedByteRangeHead ChainedByteRangeHead::splitAtMost(size_t len) {
// entire chain requested
if (len >= chainLength_) {
return std::move(*this);
}
ChainedByteRangeHead ret;
ret.chainLength_ = len;
if (len == 0) {
return ret;
}
chainLength_ -= len;
if (head.length() > len) {
// Just need to trim a little off the head.
ret.head.range_ =
folly::ByteRange(head.range_.begin(), head.range_.begin() + len);
ret.head.next_ = &ret.head;
ret.head.prev_ = &ret.head;
head.trimStart(len);
return ret;
}
ChainedByteRange* current = &head;
/**
* Find the last ChainedByteRange containing range requested. This will
* definitively terminate without looping back to head since we know length >
* len.
*/
while (len != 0) {
if (current->length() > len) {
break;
}
len -= current->length();
current = current->next_;
}
if (len == 0) {
/**
* In this case, we're splitting at the boundary of two ChainedByteRanges.
* We make head take up the place of the first ChainedByteRange in the
* second chain.
*/
ChainedByteRange* tailOfSecondPart =
(head.prev_ == current) ? &head : head.prev_;
ChainedByteRange* tailOfFirstPart =
(current->prev_ == &head ? &ret.head : current->prev_);
ret.head.range_ = head.range_;
ret.head.next_ = head.next_;
ret.head.prev_ = tailOfFirstPart;
ret.head.next_->prev_ = &ret.head;
tailOfFirstPart->next_ = &ret.head;
head.range_ = current->range_;
head.next_ = current->next_;
head.prev_ = tailOfSecondPart;
head.next_->prev_ = &head;
tailOfSecondPart->next_ = &head;
delete current;
} else {
/**
* In this case, we're splitting somewhere in the middle of a
* ChainedByteRange.
*/
ChainedByteRange* tailOfFirstPart = current;
ChainedByteRange* tailOfSecondPart =
(head.prev_ == tailOfFirstPart) ? &head : head.prev_;
ret.head.range_ = head.range_;
head.range_ =
folly::ByteRange(current->range_.begin() + len, current->range_.end());
current->range_ = folly::ByteRange(
current->range_.begin(), current->range_.begin() + len);
ret.head.next_ = head.next_;
ret.head.prev_ = tailOfFirstPart;
ret.head.next_->prev_ = &ret.head;
head.next_ = tailOfFirstPart->next_;
tailOfFirstPart->next_->prev_ = &head;
head.prev_ = tailOfSecondPart;
tailOfFirstPart->next_ = &ret.head;
}
return ret;
}
size_t ChainedByteRangeHead::trimStartAtMost(size_t len) {
size_t amountToSplit = std::min(len, chainLength());
auto splitRch = splitAtMost(amountToSplit);
return amountToSplit;
}
void ChainedByteRangeHead::resetChain() {
ChainedByteRange* curr = head.next_;
while (curr != &head) {
auto* next = curr->next_;
delete curr;
curr = next;
}
head.next_ = &head;
head.prev_ = &head;
chainLength_ = 0;
}
void ChainedByteRangeHead::moveChain(ChainedByteRangeHead&& other) {
head.range_ = other.head.range_;
ChainedByteRange* headNext = other.head.next_;
ChainedByteRange* headPrev = other.head.prev_;
headNext->prev_ = &head;
headPrev->next_ = &head;
head.next_ = other.head.next_;
head.prev_ = other.head.prev_;
other.head.range_ = folly::ByteRange();
other.head.next_ = &other.head;
other.head.prev_ = &other.head;
chainLength_ = other.chainLength_;
other.chainLength_ = 0;
}
} // namespace quic