mirror of
https://github.com/facebookincubator/mvfst.git
synced 2025-11-24 04:01:07 +03:00
Summary: This commit converts IntervalSet to use CHECKs instead of throwing exceptions and provides safe tryInsert methods that return quic::Expected for error handling. **Core Problem Solved:** IntervalSet was throwing `std::invalid_argument` exceptions in two scenarios: 1. When constructing an Interval with `start > end` 2. When interval bounds exceed the maximum allowed value This change eliminates exceptions in favor of CHECKs (for internal validation) and Expected-based error handling (for caller validation). **Implementation Details:** **1. IntervalSet Core Changes:** - Replaced `throw std::invalid_argument` with `CHECK_LE` in Interval constructor - Replaced `throw std::invalid_argument` with `CHECK_LE` in `insert(start, end)` - Added `IntervalSetError` enum for error classification - Added `folly::Expected` include **2. Safe API Layer:** - Added `tryInsert(interval)` method returning `Expected<Unit, IntervalSetError>` - Added `tryInsert(start, end)` method with pre-validation - Added `tryInsert(point)` method - Added static `Interval::tryCreate()` method for safe interval construction **3. Updated Code:** - **QuicWriteCodec.cpp**: Updated `fillFrameWithPacketReceiveTimestamps` to use `tryInsert` - Returns `QuicError` if interval validation fails - Maintains existing error handling patterns - **QuicTransportFunctions.cpp**: Updated `implicitAckCryptoStream` to use `tryInsert` - Logs errors and continues processing other packets - Robust error handling for crypto stream implicit acks Reviewed By: kvtsoy Differential Revision: D76792362 fbshipit-source-id: 5bd7c22e69a91d60cc41c603a1f2380893f4c8a0
170 lines
5.0 KiB
C++
170 lines
5.0 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.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <algorithm>
|
|
#include <cstdint>
|
|
#include <limits>
|
|
#include <queue>
|
|
|
|
#include <folly/Likely.h>
|
|
#include <glog/logging.h>
|
|
#include <quic/common/Expected.h>
|
|
|
|
namespace quic {
|
|
|
|
constexpr uint64_t kDefaultIntervalSetVersion = 0;
|
|
|
|
/**
|
|
* Error codes for IntervalSet operations
|
|
*/
|
|
enum class IntervalSetError : uint8_t {
|
|
InvalidInterval, // start > end
|
|
IntervalBoundTooLarge // interval bound exceeds max allowed value
|
|
};
|
|
|
|
template <typename T, T Unit = (T)1>
|
|
struct Interval {
|
|
T start;
|
|
T end;
|
|
|
|
static constexpr T unitValue() {
|
|
return Unit;
|
|
}
|
|
|
|
Interval(const T& s, const T& e) : start(s), end(e) {
|
|
CHECK_LE(start, end) << "Trying to construct invalid interval";
|
|
CHECK_LE(end, std::numeric_limits<T>::max() - unitValue())
|
|
<< "Interval bound too large";
|
|
}
|
|
|
|
// Safe constructor that returns Expected instead of CHECKing
|
|
[[nodiscard]] static quic::Expected<Interval<T, Unit>, IntervalSetError>
|
|
tryCreate(const T& s, const T& e) {
|
|
if (s > e) {
|
|
return quic::make_unexpected(IntervalSetError::InvalidInterval);
|
|
}
|
|
if (e > std::numeric_limits<T>::max() - unitValue()) {
|
|
return quic::make_unexpected(IntervalSetError::IntervalBoundTooLarge);
|
|
}
|
|
return Interval<T, Unit>{s, e};
|
|
}
|
|
|
|
bool operator==(Interval& rhs) const {
|
|
return start == rhs.start && end == rhs.end;
|
|
}
|
|
|
|
friend bool operator==(const Interval& a, const Interval& b) {
|
|
return a.start == b.start && a.end == b.end;
|
|
}
|
|
};
|
|
|
|
/*
|
|
* IntervalSet conceptually represents a set of sorted disjoint intervals.
|
|
* Any operations on top of an interval set should keep the intervals it holds
|
|
* sorted and disjoint. For example, insertion might trigger a merge of multiple
|
|
* intervals. This implementation exposes API for insertion at arbitrary place
|
|
* but consumption only takes place at the beginning or the end. This
|
|
* simplyfies the internal implementation. Also, still for the sake of
|
|
* simplicity, it only exposes const iterator to users.
|
|
*/
|
|
template <
|
|
typename T,
|
|
T Unit = (T)1,
|
|
template <typename... I> class Container = std::deque>
|
|
class IntervalSet : private Container<Interval<T, Unit>> {
|
|
public:
|
|
using interval_type = Interval<T, Unit>;
|
|
using container_type = Container<Interval<T, Unit>>;
|
|
using value_type = typename container_type::value_type;
|
|
|
|
// Only allow const access to simplify implementation
|
|
using const_iterator = typename container_type::const_iterator;
|
|
using const_reverse_iterator =
|
|
typename container_type::const_reverse_iterator;
|
|
|
|
IntervalSet() {}
|
|
|
|
IntervalSet(std::initializer_list<interval_type> intervals);
|
|
|
|
// Range-based for loops expect begin and end to be available, and there's no
|
|
// way to force them to instead use cbegin and cend. To provide support for
|
|
// range-based for loops while still meeting our requirement that changes to
|
|
// the set's contents should only be made through the exposed accessors,
|
|
// expose begin and end as const iterators.
|
|
|
|
auto begin() const {
|
|
return container_type::cbegin();
|
|
}
|
|
|
|
auto end() const {
|
|
return container_type::cend();
|
|
}
|
|
|
|
void insert(const Interval<T, Unit>& interval);
|
|
|
|
void insert(const T& start, const T& end);
|
|
|
|
void insert(const T& point);
|
|
|
|
// Safe versions that return Expected instead of CHECKing
|
|
[[nodiscard]] Expected<void, IntervalSetError> tryInsert(
|
|
const Interval<T, Unit>& interval);
|
|
|
|
[[nodiscard]] Expected<void, IntervalSetError> tryInsert(
|
|
const T& start,
|
|
const T& end);
|
|
|
|
[[nodiscard]] Expected<void, IntervalSetError> tryInsert(const T& point);
|
|
|
|
void withdraw(const Interval<T, Unit>& interval);
|
|
|
|
[[nodiscard]] bool contains(const T& start, const T& end) const;
|
|
|
|
/**
|
|
* The version changes whenever we insert into the ack list.
|
|
*/
|
|
uint64_t insertVersion() const;
|
|
|
|
bool operator==(const IntervalSet& rhs) const {
|
|
return static_cast<container_type>(*this) ==
|
|
static_cast<container_type>(rhs);
|
|
}
|
|
|
|
bool operator!=(const IntervalSet& rhs) const {
|
|
return static_cast<container_type>(*this) !=
|
|
static_cast<container_type>(rhs);
|
|
}
|
|
|
|
using container_type::back;
|
|
using container_type::cbegin;
|
|
using container_type::cend;
|
|
using container_type::clear;
|
|
using container_type::crbegin;
|
|
using container_type::crend;
|
|
using container_type::empty;
|
|
using container_type::front;
|
|
using container_type::pop_back;
|
|
using container_type::size;
|
|
|
|
private:
|
|
/**
|
|
* Helper function to find the intersecting range in this interval set
|
|
* for the interval passed in. It returns a pair of iterator denoting
|
|
* [first, end) in the traditional stl iterator sense where first is the first
|
|
* block that overlaps the interval and end is the last block that is > the
|
|
* interval.
|
|
*/
|
|
auto intersectingRange(const interval_type& interval) -> decltype(auto);
|
|
|
|
uint64_t insertVersion_{kDefaultIntervalSetVersion};
|
|
};
|
|
} // namespace quic
|
|
|
|
#include <quic/common/IntervalSet-inl.h>
|