blob: 4b0b1b6e04a28c3b8a0e7959dd0dd799dc984779 [file] [log] [blame]
// Copyright (c) 2019 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.
#ifndef NET_THIRD_PARTY_QUIC_CORE_QUIC_INTERVAL_H_
#define NET_THIRD_PARTY_QUIC_CORE_QUIC_INTERVAL_H_
// An QuicInterval<T> is a data structure used to represent a contiguous,
// mutable range over an ordered type T. Supported operations include testing a
// value to see whether it is included in the QuicInterval, comparing two
// QuicIntervals, and performing their union, intersection, and difference. For
// the purposes of this library, an "ordered type" is any type that induces a
// total order on its values via its less-than operator (operator<()). Examples
// of such types are basic arithmetic types like int and double as well as class
// types like string.
//
// An QuicInterval<T> is represented using the usual C++ STL convention, namely
// as the half-open QuicInterval [min, max). A point p is considered to be
// contained in the QuicInterval iff p >= min && p < max. One consequence of
// this definition is that for any non-empty QuicInterval, min is contained in
// the QuicInterval but max is not. There is no canonical representation for the
// empty QuicInterval; rather, any QuicInterval where max <= min is regarded as
// empty. As a consequence, two empty QuicIntervals will still compare as equal
// despite possibly having different underlying min() or max() values. Also
// beware of the terminology used here: the library uses the terms "min" and
// "max" rather than "begin" and "end" as is conventional for the STL.
//
// T is required to be default- and copy-constructable, to have an assignment
// operator, and the full complement of comparison operators (<, <=, ==, !=, >=,
// >). A difference operator (operator-()) is required if
// QuicInterval<T>::Length is used.
//
// QuicInterval supports operator==. Two QuicIntervals are considered equal if
// either they are both empty or if their corresponding min and max fields
// compare equal. QuicInterval also provides an operator<. Unfortunately,
// operator< is currently buggy because its behavior is inconsistent with
// operator==: two empty ranges with different representations may be regarded
// as equal by operator== but regarded as different by operator<. Bug 9240050
// has been created to address this.
//
//
// Examples:
// QuicInterval<int> r1(0, 100); // The QuicInterval [0, 100).
// EXPECT_TRUE(r1.Contains(0));
// EXPECT_TRUE(r1.Contains(50));
// EXPECT_FALSE(r1.Contains(100)); // 100 is just outside the QuicInterval.
//
// QuicInterval<int> r2(50, 150); // The QuicInterval [50, 150).
// EXPECT_TRUE(r1.Intersects(r2));
// EXPECT_FALSE(r1.Contains(r2));
// EXPECT_TRUE(r1.IntersectWith(r2)); // Mutates r1.
// EXPECT_EQ(QuicInterval<int>(50, 100), r1); // r1 is now [50, 100).
//
// QuicInterval<int> r3(1000, 2000); // The QuicInterval [1000, 2000).
// EXPECT_TRUE(r1.IntersectWith(r3)); // Mutates r1.
// EXPECT_TRUE(r1.Empty()); // Now r1 is empty.
// EXPECT_FALSE(r1.Contains(r1.min())); // e.g. doesn't contain its own min.
#include <algorithm>
#include <ostream>
#include <type_traits>
#include <utility>
#include <vector>
#include "starboard/types.h"
namespace quic {
template <typename T>
class QuicInterval {
private:
// Type trait for deriving the return type for QuicInterval::Length. If
// operator-() is not defined for T, then the return type is void. This makes
// the signature for Length compile so that the class can be used for such T,
// but code that calls Length would still generate a compilation error.
template <typename U>
class DiffTypeOrVoid {
private:
template <typename V>
static auto f(const V* v) -> decltype(*v - *v);
template <typename V>
static void f(...);
public:
using type = typename std::decay<decltype(f<U>(nullptr))>::type;
};
public:
// Construct an QuicInterval representing an empty QuicInterval.
QuicInterval() : min_(), max_() {}
// Construct an QuicInterval representing the QuicInterval [min, max). If min
// < max, the constructed object will represent the non-empty QuicInterval
// containing all values from min up to (but not including) max. On the other
// hand, if min >= max, the constructed object will represent the empty
// QuicInterval.
QuicInterval(const T& min, const T& max) : min_(min), max_(max) {}
template <typename U1,
typename U2,
typename = typename std::enable_if<
std::is_convertible<U1, T>::value &&
std::is_convertible<U2, T>::value>::type>
QuicInterval(U1&& min, U2&& max)
: min_(std::forward<U1>(min)), max_(std::forward<U2>(max)) {}
const T& min() const { return min_; }
const T& max() const { return max_; }
void SetMin(const T& t) { min_ = t; }
void SetMax(const T& t) { max_ = t; }
void Set(const T& min, const T& max) {
SetMin(min);
SetMax(max);
}
void Clear() { *this = {}; }
bool Empty() const { return min() >= max(); }
// Returns the length of this QuicInterval. The value returned is zero if
// Empty() is true; otherwise the value returned is max() - min().
typename DiffTypeOrVoid<T>::type Length() const {
return (Empty() ? min() : max()) - min();
}
// Returns true iff t >= min() && t < max().
bool Contains(const T& t) const { return min() <= t && max() > t; }
// Returns true iff *this and i are non-empty, and *this includes i. "*this
// includes i" means that for all t, if i.Contains(t) then this->Contains(t).
// Note the unintuitive consequence of this definition: this method always
// returns false when i is the empty QuicInterval.
bool Contains(const QuicInterval& i) const {
return !Empty() && !i.Empty() && min() <= i.min() && max() >= i.max();
}
// Returns true iff there exists some point t for which this->Contains(t) &&
// i.Contains(t) evaluates to true, i.e. if the intersection is non-empty.
bool Intersects(const QuicInterval& i) const {
return !Empty() && !i.Empty() && min() < i.max() && max() > i.min();
}
// Returns true iff there exists some point t for which this->Contains(t) &&
// i.Contains(t) evaluates to true, i.e. if the intersection is non-empty.
// Furthermore, if the intersection is non-empty and the out pointer is not
// null, this method stores the calculated intersection in *out.
bool Intersects(const QuicInterval& i, QuicInterval* out) const;
// Sets *this to be the intersection of itself with i. Returns true iff
// *this was modified.
bool IntersectWith(const QuicInterval& i);
// Calculates the smallest QuicInterval containing both *this i, and updates
// *this to represent that QuicInterval, and returns true iff *this was
// modified.
bool SpanningUnion(const QuicInterval& i);
// Determines the difference between two QuicIntervals by finding all points
// that are contained in *this but not in i, coalesces those points into the
// largest possible contiguous QuicIntervals, and appends those QuicIntervals
// to the *difference vector. Intuitively this can be thought of as "erasing"
// i from *this. This will either completely erase *this (leaving nothing
// behind), partially erase some of *this from the left or right side (leaving
// some residual behind), or erase a hole in the middle of *this (leaving
// behind an QuicInterval on either side). Therefore, 0, 1, or 2 QuicIntervals
// will be appended to *difference. The method returns true iff the
// intersection of *this and i is non-empty. The caller owns the vector and
// the QuicInterval* pointers inside it. The difference vector is required to
// be non-null.
bool Difference(const QuicInterval& i,
std::vector<QuicInterval*>* difference) const;
// Determines the difference between two QuicIntervals as in
// Difference(QuicInterval&, vector*), but stores the results directly in out
// parameters rather than dynamically allocating an QuicInterval* and
// appending it to a vector. If two results are generated, the one with the
// smaller value of min() will be stored in *lo and the other in *hi.
// Otherwise (if fewer than two results are generated), unused arguments will
// be set to the empty QuicInterval (it is possible that *lo will be empty and
// *hi non-empty). The method returns true iff the intersection of *this and i
// is non-empty.
bool Difference(const QuicInterval& i,
QuicInterval* lo,
QuicInterval* hi) const;
friend bool operator==(const QuicInterval& a, const QuicInterval& b) {
bool ae = a.Empty();
bool be = b.Empty();
if (ae && be)
return true; // All empties are equal.
if (ae != be)
return false; // Empty cannot equal nonempty.
return a.min() == b.min() && a.max() == b.max();
}
friend bool operator!=(const QuicInterval& a, const QuicInterval& b) {
return !(a == b);
}
// Defines a comparator which can be used to induce an order on QuicIntervals,
// so that, for example, they can be stored in an ordered container such as
// std::set. The ordering is arbitrary, but does provide the guarantee that,
// for non-empty QuicIntervals X and Y, if X contains Y, then X <= Y.
// TODO(kosak): The current implementation of this comparator has a problem
// because the ordering it induces is inconsistent with that of Equals(). In
// particular, this comparator does not properly consider all empty
// QuicIntervals equivalent. Bug 9240050 has been created to track this.
friend bool operator<(const QuicInterval& a, const QuicInterval& b) {
return a.min() < b.min() || (!(b.min() < a.min()) && b.max() < a.max());
}
private:
T min_; // Inclusive lower bound.
T max_; // Exclusive upper bound.
};
// Constructs an QuicInterval by deducing the types from the function arguments.
template <typename T>
QuicInterval<T> MakeQuicInterval(T&& lhs, T&& rhs) {
return QuicInterval<T>(std::forward<T>(lhs), std::forward<T>(rhs));
}
// Note: ideally we'd use
// decltype(out << "[" << i.min() << ", " << i.max() << ")")
// as return type of the function, but as of July 2017 this triggers g++
// "sorry, unimplemented: string literal in function template signature" error.
template <typename T>
auto operator<<(std::ostream& out, const QuicInterval<T>& i)
-> decltype(out << i.min()) {
return out << "[" << i.min() << ", " << i.max() << ")";
}
//==============================================================================
// Implementation details: Clients can stop reading here.
template <typename T>
bool QuicInterval<T>::Intersects(const QuicInterval& i,
QuicInterval* out) const {
if (!Intersects(i))
return false;
if (out != nullptr) {
*out = QuicInterval(std::max(min(), i.min()), std::min(max(), i.max()));
}
return true;
}
template <typename T>
bool QuicInterval<T>::IntersectWith(const QuicInterval& i) {
if (Empty())
return false;
bool modified = false;
if (i.min() > min()) {
SetMin(i.min());
modified = true;
}
if (i.max() < max()) {
SetMax(i.max());
modified = true;
}
return modified;
}
template <typename T>
bool QuicInterval<T>::SpanningUnion(const QuicInterval& i) {
if (i.Empty())
return false;
if (Empty()) {
*this = i;
return true;
}
bool modified = false;
if (i.min() < min()) {
SetMin(i.min());
modified = true;
}
if (i.max() > max()) {
SetMax(i.max());
modified = true;
}
return modified;
}
template <typename T>
bool QuicInterval<T>::Difference(const QuicInterval& i,
std::vector<QuicInterval*>* difference) const {
if (Empty()) {
// <empty> - <i> = <empty>
return false;
}
if (i.Empty()) {
// <this> - <empty> = <this>
difference->push_back(new QuicInterval(*this));
return false;
}
if (min() < i.max() && min() >= i.min() && max() > i.max()) {
// [------ this ------)
// [------ i ------)
// [-- result ---)
difference->push_back(new QuicInterval(i.max(), max()));
return true;
}
if (max() > i.min() && max() <= i.max() && min() < i.min()) {
// [------ this ------)
// [------ i ------)
// [- result -)
difference->push_back(new QuicInterval(min(), i.min()));
return true;
}
if (min() < i.min() && max() > i.max()) {
// [------- this --------)
// [---- i ----)
// [ R1 ) [ R2 )
// There are two results: R1 and R2.
difference->push_back(new QuicInterval(min(), i.min()));
difference->push_back(new QuicInterval(i.max(), max()));
return true;
}
if (min() >= i.min() && max() <= i.max()) {
// [--- this ---)
// [------ i --------)
// Intersection is <this>, so difference yields the empty QuicInterval.
// Nothing is appended to *difference.
return true;
}
// No intersection. Append <this>.
difference->push_back(new QuicInterval(*this));
return false;
}
template <typename T>
bool QuicInterval<T>::Difference(const QuicInterval& i,
QuicInterval* lo,
QuicInterval* hi) const {
// Initialize *lo and *hi to empty
*lo = {};
*hi = {};
if (Empty())
return false;
if (i.Empty()) {
*lo = *this;
return false;
}
if (min() < i.max() && min() >= i.min() && max() > i.max()) {
// [------ this ------)
// [------ i ------)
// [-- result ---)
*hi = QuicInterval(i.max(), max());
return true;
}
if (max() > i.min() && max() <= i.max() && min() < i.min()) {
// [------ this ------)
// [------ i ------)
// [- result -)
*lo = QuicInterval(min(), i.min());
return true;
}
if (min() < i.min() && max() > i.max()) {
// [------- this --------)
// [---- i ----)
// [ R1 ) [ R2 )
// There are two results: R1 and R2.
*lo = QuicInterval(min(), i.min());
*hi = QuicInterval(i.max(), max());
return true;
}
if (min() >= i.min() && max() <= i.max()) {
// [--- this ---)
// [------ i --------)
// Intersection is <this>, so difference yields the empty QuicInterval.
return true;
}
*lo = *this; // No intersection.
return false;
}
} // namespace quic
#endif // NET_THIRD_PARTY_QUIC_CORE_QUIC_INTERVAL_H_