/* Copyright 2016 MongoDB, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the GNU Affero General Public License in all respects
* for all of the code used other than as permitted herein. If you modify
* file(s) with this exception, you may extend this exception to your
* version of the file(s), but you are not obligated to do so. If you do not
* wish to do so, delete this exception statement from your version. If you
* delete this exception statement from all source files in the program,
* then also delete it in the license file.
*/
#pragma once
#include
#include
#include
#include
#include "mongo/platform/overflow_arithmetic.h"
#include "mongo/stdx/chrono.h"
#include "mongo/stdx/type_traits.h"
#include "mongo/util/assert_util.h"
#include "mongo/util/mongoutils/str.h"
namespace mongo {
template
class StringBuilderImpl;
template
class Duration;
using Nanoseconds = Duration;
using Microseconds = Duration;
using Milliseconds = Duration;
using Seconds = Duration>;
using Minutes = Duration>;
using Hours = Duration>;
//
// Streaming output operators for common duration types. Writes the numerical value followed by
// an abbreviated unit, without a space.
//
// E.g., std::cout << Minutes{5} << std::endl; should produce the following:
// 5min
//
std::ostream& operator<<(std::ostream& os, Nanoseconds ns);
std::ostream& operator<<(std::ostream& os, Microseconds us);
std::ostream& operator<<(std::ostream& os, Milliseconds ms);
std::ostream& operator<<(std::ostream& os, Seconds s);
std::ostream& operator<<(std::ostream& os, Minutes m);
std::ostream& operator<<(std::ostream& os, Hours h);
template
StringBuilderImpl& operator<<(StringBuilderImpl& os, Nanoseconds ns);
template
StringBuilderImpl& operator<<(StringBuilderImpl& os, Microseconds us);
template
StringBuilderImpl& operator<<(StringBuilderImpl& os, Milliseconds ms);
template
StringBuilderImpl& operator<<(StringBuilderImpl& os, Seconds s);
template
StringBuilderImpl& operator<<(StringBuilderImpl& os, Minutes m);
template
StringBuilderImpl& operator<<(StringBuilderImpl& os, Hours h);
template
using HigherPrecisionDuration =
typename std::conditional::value,
Duration1,
Duration2>::type;
/**
* Casts from one Duration precision to another.
*
* May throw a UserException if "from" is of lower-precision type and is outside the range of the
* ToDuration. For example, Seconds::max() cannot be represented as a Milliseconds, and
* so attempting to cast that value to Milliseconds will throw an exception.
*/
template
ToDuration duration_cast(const Duration& from) {
using FromOverTo = std::ratio_divide;
if (ToDuration::template isHigherPrecisionThan>()) {
typename ToDuration::rep toCount;
uassert(ErrorCodes::DurationOverflow,
"Overflow casting from a lower-precision duration to a higher-precision duration",
!mongoSignedMultiplyOverflow64(from.count(), FromOverTo::num, &toCount));
return ToDuration{toCount};
}
return ToDuration{from.count() / FromOverTo::den};
}
template
inline ToDuration duration_cast(const stdx::chrono::duration& d) {
return duration_cast(Duration{d.count()});
}
/**
* Convenience method for reading the count of a duration with specified units.
*
* Use when logging or comparing to integers, to ensure that you're using
* the units you intend.
*
* E.g., log() << durationCount(some duration) << " seconds";
*/
template
inline long long durationCount(DIn d) {
return duration_cast(d).count();
}
template
inline long long durationCount(const stdx::chrono::duration& d) {
return durationCount(Duration{d.count()});
}
/**
* Type representing a duration using a 64-bit counter.
*
* The Period template argument is a std::ratio describing the units of the duration type.
*
* This type's behavior is similar to std::chrono::duration, but instead of undefined behavior on
* overflows and other conversions, throws exceptions.
*/
template
class Duration {
public:
static_assert(Period::num > 0, "Duration::period's numerator must be positive");
static_assert(Period::den > 0, "Duration::period's denominator must be positive");
using rep = int64_t;
using period = Period;
/**
* Type with static bool "value" set to true if this Duration type is higher precision than
* OtherDuration. That is, if OtherDuration::period > period.
*/
template
struct IsHigherPrecisionThan {
using OtherOverThis = std::ratio_divide;
static_assert(OtherOverThis::den == 1 || OtherOverThis::num == 1,
"Mongo duration types are only compatible with each other when one's period "
"is an even multiple of the other's.");
static constexpr bool value = OtherOverThis::den == 1 && OtherOverThis::num != 1;
};
/**
* Type with static bool "value" set to true if this Duration type is lower precision than
* OtherDuration. That is, if OtherDuration::period > period.
*/
template
struct IsLowerPrecisionThan {
using OtherOverThis = std::ratio_divide;
static_assert(OtherOverThis::den == 1 || OtherOverThis::num == 1,
"Mongo duration types are only compatible with each other when one's period "
"is an even multiple of the other's.");
static constexpr bool value = OtherOverThis::num == 1 && OtherOverThis::den != 1;
};
/**
* Function that returns true if period > OtherDuration::period.
*/
template
constexpr static bool isHigherPrecisionThan() {
return IsHigherPrecisionThan::value;
}
/**
* Function that returns true if period < OtherDuration::period.
*/
template
constexpr static bool isLowerPrecisionThan() {
return IsLowerPrecisionThan::value;
}
static constexpr Duration zero() {
return Duration{};
}
static constexpr Duration min() {
return Duration{std::numeric_limits::min()};
}
static constexpr Duration max() {
return Duration{std::numeric_limits::max()};
}
/**
* Constructs the zero duration.
*/
constexpr Duration() = default;
/**
* Constructs a duration representing "r" periods.
*/
template <
typename Rep2,
stdx::enable_if_t::value && std::is_integral::value,
int> = 0>
constexpr explicit Duration(const Rep2& r) : _count(r) {
static_assert(std::is_signed::value || sizeof(Rep2) < sizeof(rep),
"Durations must be constructed from values of integral type that are "
"representable as 64-bit signed integers");
}
/**
* Constructs a higher-precision duration from a lower-precision one, as by duration_cast.
*
* Throws a UserException if "from" is out of the range of this duration type.
*
* It is a compilation error to attempt a conversion from higher-precision to lower-precision by
* this constructor.
*/
template
/*implicit*/ Duration(const Duration& from)
: Duration(duration_cast(from)) {
static_assert(!isLowerPrecisionThan>(),
"Use duration_cast to convert from higher precision Duration types to lower "
"precision ones");
}
stdx::chrono::system_clock::duration toSystemDuration() const {
using SystemDuration = stdx::chrono::system_clock::duration;
return SystemDuration{duration_cast>(*this).count()};
}
/**
* Returns the number of periods represented by this duration.
*
* It is better to use durationCount(value), since it makes the unit of the
* count clear at the call site.
*/
constexpr rep count() const {
return _count;
}
/**
* Compares this duration to another duration of the same type.
*
* Returns 1, -1 or 0 depending on whether this duration is greater than, less than or equal to
* the other duration, respectively.
*/
constexpr int compare(const Duration& other) const {
return (count() > other.count()) ? 1 : (count() < other.count()) ? -1 : 0;
}
/**
* Compares this duration to a lower-precision duration, "other".
*/
template
int compare(const Duration& other) const {
if (isLowerPrecisionThan>()) {
return -other.compare(*this);
}
using OtherOverThis = std::ratio_divide;
rep otherCount;
if (mongoSignedMultiplyOverflow64(other.count(), OtherOverThis::num, &otherCount)) {
return other.count() < 0 ? 1 : -1;
}
if (count() < otherCount) {
return -1;
}
if (count() > otherCount) {
return 1;
}
return 0;
}
constexpr Duration operator+() const {
return *this;
}
Duration operator-() const {
uassert(ErrorCodes::DurationOverflow, "Cannot negate the minimum duration", *this != min());
return Duration(-count());
}
//
// In-place arithmetic operators
//
Duration& operator++() {
return (*this) += Duration{1};
}
Duration operator++(int) {
auto result = *this;
*this += Duration{1};
return result;
}
Duration operator--() {
return (*this) -= Duration{1};
}
Duration operator--(int) {
auto result = *this;
*this -= Duration{1};
return result;
}
Duration& operator+=(const Duration& other) {
uassert(ErrorCodes::DurationOverflow,
str::stream() << "Overflow while adding " << other << " to " << *this,
!mongoSignedAddOverflow64(count(), other.count(), &_count));
return *this;
}
Duration& operator-=(const Duration& other) {
uassert(ErrorCodes::DurationOverflow,
str::stream() << "Overflow while subtracting " << other << " from " << *this,
!mongoSignedSubtractOverflow64(count(), other.count(), &_count));
return *this;
}
template
Duration& operator*=(const Rep2& scale) {
static_assert(std::is_integral::value && std::is_signed::value,
"Durations may only be multiplied by values of signed integral type");
uassert(ErrorCodes::DurationOverflow,
str::stream() << "Overflow while multiplying " << *this << " by " << scale,
!mongoSignedMultiplyOverflow64(count(), scale, &_count));
return *this;
}
template
Duration& operator/=(const Rep2& scale) {
static_assert(std::is_integral::value && std::is_signed::value,
"Durations may only be divided by values of signed integral type");
uassert(ErrorCodes::DurationOverflow,
str::stream() << "Overflow while dividing " << *this << " by -1",
(count() != min().count() || scale != -1));
_count /= scale;
return *this;
}
private:
rep _count = {};
};
template
constexpr bool operator==(const Duration& lhs, const Duration& rhs) {
return lhs.compare(rhs) == 0;
}
template
constexpr bool operator!=(const Duration& lhs, const Duration& rhs) {
return lhs.compare(rhs) != 0;
}
template
constexpr bool operator<(const Duration& lhs, const Duration& rhs) {
return lhs.compare(rhs) < 0;
}
template
constexpr bool operator<=(const Duration& lhs, const Duration& rhs) {
return lhs.compare(rhs) <= 0;
}
template
constexpr bool operator>(const Duration& lhs, const Duration& rhs) {
return lhs.compare(rhs) > 0;
}
template
constexpr bool operator>=(const Duration& lhs, const Duration& rhs) {
return lhs.compare(rhs) >= 0;
}
/**
* Returns the sum of two durations, "lhs" and "rhs".
*/
template <
typename LhsPeriod,
typename RhsPeriod,
typename ReturnDuration = HigherPrecisionDuration, Duration>>
ReturnDuration operator+(const Duration& lhs, const Duration& rhs) {
ReturnDuration result = lhs;
result += rhs;
return result;
}
/**
* Returns the result of subtracting "rhs" from "lhs".
*/
template <
typename LhsPeriod,
typename RhsPeriod,
typename ReturnDuration = HigherPrecisionDuration, Duration>>
ReturnDuration operator-(const Duration& lhs, const Duration& rhs) {
ReturnDuration result = lhs;
result -= rhs;
return result;
}
/**
* Returns the product of a duration "d" and a unitless integer, "scale".
*/
template
Duration operator*(Duration d, const Rep2& scale) {
d *= scale;
return d;
}
template
Duration operator*(const Rep2& scale, Duration d) {
d *= scale;
return d;
}
/**
* Returns duration "d" divided by unitless integer "scale".
*/
template
Duration operator/(Duration d, const Rep2& scale) {
d /= scale;
return d;
}
} // namespace mongo