| // Copyright (c) 2012 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. |
| |
| // QuicBandwidth represents a bandwidth, stored in bits per second resolution. |
| |
| #ifndef QUICHE_QUIC_CORE_QUIC_BANDWIDTH_H_ |
| #define QUICHE_QUIC_CORE_QUIC_BANDWIDTH_H_ |
| |
| #include <cmath> |
| #include <cstdint> |
| #include <limits> |
| #include <optional> |
| #include <ostream> |
| #include <string> |
| |
| #include "quiche/quic/core/quic_constants.h" |
| #include "quiche/quic/core/quic_time.h" |
| #include "quiche/quic/core/quic_types.h" |
| #include "quiche/common/platform/api/quiche_export.h" |
| #include "quiche/common/platform/api/quiche_logging.h" |
| |
| namespace quic { |
| |
| // QuicBandwidth is a thin wrapper around an int64_t representing bits per |
| // second. Methods and operators declared in this file do not perform range |
| // checks on parameters unless otherwise specified. |
| class QUICHE_EXPORT QuicBandwidth { |
| public: |
| // Creates a new QuicBandwidth with an internal value of 0. |
| static constexpr QuicBandwidth Zero() { return QuicBandwidth(0); } |
| |
| // Creates a new QuicBandwidth with an internal value of INT64_MAX. The |
| // infinite QuicBandwidth is only useful as a sentinel value. It cannot be |
| // added to any non-zero QuicBandwidth without committing UB. |
| static constexpr QuicBandwidth Infinite() { |
| return QuicBandwidth(std::numeric_limits<int64_t>::max()); |
| } |
| |
| // Create a new QuicBandwidth holding the bits per second. |
| static constexpr QuicBandwidth FromBitsPerSecond(int64_t bits_per_second) { |
| return QuicBandwidth(bits_per_second); |
| } |
| |
| // Create a new QuicBandwidth holding the kilo bits per second. |
| static constexpr QuicBandwidth FromKBitsPerSecond(int64_t k_bits_per_second) { |
| return QuicBandwidth(k_bits_per_second * 1000); |
| } |
| |
| // Create a new QuicBandwidth holding the bytes per second. |
| static constexpr QuicBandwidth FromBytesPerSecond(int64_t bytes_per_second) { |
| return QuicBandwidth(bytes_per_second * 8); |
| } |
| |
| // Create a new QuicBandwidth holding the kilo bytes per second. |
| static constexpr QuicBandwidth FromKBytesPerSecond( |
| int64_t k_bytes_per_second) { |
| return QuicBandwidth(k_bytes_per_second * 8000); |
| } |
| |
| // Create a new QuicBandwidth based on the bytes per the elapsed delta. |
| static QuicBandwidth FromBytesAndTimeDelta(QuicByteCount bytes, |
| QuicTime::Delta delta) { |
| if (bytes == 0) { |
| return QuicBandwidth(0); |
| } |
| |
| // 1 bit is 1000000 micro bits. |
| int64_t num_micro_bits = 8 * bytes * kNumMicrosPerSecond; |
| if (num_micro_bits < delta.ToMicroseconds()) { |
| return QuicBandwidth(1); |
| } |
| |
| return QuicBandwidth(num_micro_bits / delta.ToMicroseconds()); |
| } |
| |
| int64_t ToBitsPerSecond() const { return bits_per_second_; } |
| |
| int64_t ToKBitsPerSecond() const { return bits_per_second_ / 1000; } |
| |
| int64_t ToBytesPerSecond() const { return bits_per_second_ / 8; } |
| |
| int64_t ToKBytesPerSecond() const { return bits_per_second_ / 8000; } |
| |
| // Returns the product of `this` and the given `time_period` when the |
| // parameters are nonnegative and the computation would not overflow. |
| // Otherwise, returns `std::nullopt`. |
| constexpr std::optional<QuicByteCount> ToBytesPerPeriodSafe( |
| QuicTime::Delta time_period) const { |
| const std::optional<int64_t> bits_per_second_times_microseconds = |
| SafeMultiplyNonNegatives(bits_per_second_, |
| time_period.ToMicroseconds()); |
| if (!bits_per_second_times_microseconds.has_value()) { |
| return std::nullopt; |
| } |
| return *bits_per_second_times_microseconds / 8 / kNumMicrosPerSecond; |
| } |
| |
| constexpr QuicByteCount ToBytesPerPeriod(QuicTime::Delta time_period) const { |
| // TODO: b/461578611 - Remove the short-circuit on negative `time_period` |
| // once we're certain incoming deltas are non-negative. |
| QUICHE_DCHECK(time_period < QuicTime::Delta::Zero() || |
| ToBytesPerPeriodSafe(time_period).has_value()) |
| << "bits_per_second: " << bits_per_second_ |
| << ", time_period: " << time_period.ToMicroseconds() << " us"; |
| return bits_per_second_ * time_period.ToMicroseconds() / 8 / |
| kNumMicrosPerSecond; |
| } |
| |
| int64_t ToKBytesPerPeriod(QuicTime::Delta time_period) const { |
| return bits_per_second_ * time_period.ToMicroseconds() / 8000 / |
| kNumMicrosPerSecond; |
| } |
| |
| bool IsZero() const { return bits_per_second_ == 0; } |
| bool IsInfinite() const { |
| return bits_per_second_ == Infinite().ToBitsPerSecond(); |
| } |
| |
| constexpr QuicTime::Delta TransferTime(QuicByteCount bytes) const { |
| if (bits_per_second_ == 0) { |
| return QuicTime::Delta::Zero(); |
| } |
| return QuicTime::Delta::FromMicroseconds(bytes * 8 * kNumMicrosPerSecond / |
| bits_per_second_); |
| } |
| |
| std::string ToDebuggingValue() const; |
| |
| template <typename Sink> |
| friend void AbslStringify(Sink& sink, QuicBandwidth bandwidth) { |
| sink.Append(bandwidth.ToDebuggingValue()); |
| } |
| |
| private: |
| // Returns the value of `a * b` if both `a` and `b` are non-negative and the |
| // result fits in `int64_t`. Otherwise, returns `std::nullopt`. |
| static constexpr std::optional<int64_t> SafeMultiplyNonNegatives(int64_t a, |
| int64_t b) { |
| if (a < 0 || b < 0) { |
| return std::nullopt; |
| } |
| if (a == 0 || b == 0) { |
| return 0; |
| } |
| if (a > std::numeric_limits<int64_t>::max() / b) { |
| return std::nullopt; |
| } |
| return a * b; |
| } |
| |
| explicit constexpr QuicBandwidth(int64_t bits_per_second) |
| : bits_per_second_(bits_per_second >= 0 ? bits_per_second : 0) {} |
| |
| int64_t bits_per_second_; |
| |
| friend constexpr QuicBandwidth operator+(QuicBandwidth lhs, |
| QuicBandwidth rhs); |
| friend constexpr QuicBandwidth operator-(QuicBandwidth lhs, |
| QuicBandwidth rhs); |
| friend QuicBandwidth operator*(QuicBandwidth lhs, float rhs); |
| }; |
| |
| // Non-member relational operators for QuicBandwidth. |
| inline bool operator==(QuicBandwidth lhs, QuicBandwidth rhs) { |
| return lhs.ToBitsPerSecond() == rhs.ToBitsPerSecond(); |
| } |
| inline bool operator!=(QuicBandwidth lhs, QuicBandwidth rhs) { |
| return !(lhs == rhs); |
| } |
| inline bool operator<(QuicBandwidth lhs, QuicBandwidth rhs) { |
| return lhs.ToBitsPerSecond() < rhs.ToBitsPerSecond(); |
| } |
| inline bool operator>(QuicBandwidth lhs, QuicBandwidth rhs) { |
| return rhs < lhs; |
| } |
| inline bool operator<=(QuicBandwidth lhs, QuicBandwidth rhs) { |
| return !(rhs < lhs); |
| } |
| inline bool operator>=(QuicBandwidth lhs, QuicBandwidth rhs) { |
| return !(lhs < rhs); |
| } |
| |
| // Non-member arithmetic operators for QuicBandwidth. |
| inline constexpr QuicBandwidth operator+(QuicBandwidth lhs, QuicBandwidth rhs) { |
| return QuicBandwidth(lhs.bits_per_second_ + rhs.bits_per_second_); |
| } |
| inline constexpr QuicBandwidth operator-(QuicBandwidth lhs, QuicBandwidth rhs) { |
| return QuicBandwidth(lhs.bits_per_second_ - rhs.bits_per_second_); |
| } |
| inline QuicBandwidth operator*(QuicBandwidth lhs, float rhs) { |
| return QuicBandwidth( |
| static_cast<int64_t>(std::llround(lhs.bits_per_second_ * rhs))); |
| } |
| inline QuicBandwidth operator*(float lhs, QuicBandwidth rhs) { |
| return rhs * lhs; |
| } |
| inline constexpr QuicByteCount operator*(QuicBandwidth lhs, |
| QuicTime::Delta rhs) { |
| return lhs.ToBytesPerPeriod(rhs); |
| } |
| inline constexpr QuicByteCount operator*(QuicTime::Delta lhs, |
| QuicBandwidth rhs) { |
| return rhs * lhs; |
| } |
| |
| // Override stream output operator for gtest. |
| inline std::ostream& operator<<(std::ostream& output, |
| const QuicBandwidth bandwidth) { |
| output << bandwidth.ToDebuggingValue(); |
| return output; |
| } |
| |
| } // namespace quic |
| #endif // QUICHE_QUIC_CORE_QUIC_BANDWIDTH_H_ |