Files
oemcrypto/util/include/wv_duration.h
2025-05-22 16:33:29 -07:00

287 lines
10 KiB
C++

// Copyright 2025 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
#ifndef WVCDM_UTIL_WV_DURATION_H_
#define WVCDM_UTIL_WV_DURATION_H_
#include <inttypes.h>
#include <chrono>
#include <ostream>
#include <ratio>
#include <string>
#include "wv_class_utils.h"
namespace wvutil {
// Wrappers around various std::chono::duration units.
using Nanoseconds = std::chrono::nanoseconds;
using Microseconds = std::chrono::microseconds;
using Milliseconds = std::chrono::milliseconds;
using Seconds = std::chrono::seconds;
using Minutes = std::chrono::minutes;
using Hours = std::chrono::hours;
#if __cplusplus >= 202002L // C++20
using Days = std::chrono::days;
#else
// Days is not declared in C++17, the standard C++ library
// uses the following implementation for C++20.
using Days = std::chrono::duration<int64_t, std::ratio<86400> >;
#endif
// A high-level duration struct for storing a duration and
// easily accessing the components.
// A duration measures a concrete amount of time between two
// different points in time.
//
// Precision of Duration is in milliseconds.
class Duration final {
public:
constexpr Duration() = default;
// Initialize duration from some kind of chrono duration type that
// can easily convert to milliseconds.
// Compiler will not allow irreconcilable duration types to be used
// without caller explicitly chrono::duration_cast<milliseconds>.
template <class Rep, class Period>
constexpr Duration(const std::chrono::duration<Rep, Period>& total_duration)
: total_milliseconds_(total_duration) {}
WVCDM_CONSTEXPR_DEFAULT_COPY_AND_MOVE(Duration);
private:
// Note: This private section is needed here.
// clang++ with -Wundefined-inline will complain about certain
// inline members not being declared before use in other inline
// methods.
// Helper function for performing truncate (round towards zero)
// operation on chrono::duration types.
// The standard C++ chrono library does not provide such a function.
template <class ToDuration, class Rep, class Period>
static constexpr ToDuration DurationTruncate(
const std::chrono::duration<Rep, Period>& duration) {
return duration >= std::chrono::duration<Rep, Period>::zero()
? std::chrono::floor<ToDuration>(duration)
: std::chrono::ceil<ToDuration>(duration);
}
// Helper function for performing truncate on Duration class's
// |total_milliseconds_|, returning it in a chrono::duration type
// which is specified by template parameter |ToDuration|.
template <class ToDuration>
constexpr Duration GetTruncateInternal() const {
return Duration(DurationTruncate<ToDuration>(total_milliseconds_));
}
public:
// == Special Initializers ==
static constexpr Duration Zero() { return Duration(); }
static constexpr Duration FromMilliseconds(int64_t milliseconds) {
return Duration(Milliseconds(milliseconds));
}
static constexpr Duration FromSeconds(int64_t seconds) {
return Duration(Seconds(seconds));
}
constexpr bool IsZero() const {
return total_milliseconds_ == Milliseconds::zero();
}
constexpr bool IsNegative() const {
return total_milliseconds_ < Milliseconds::zero();
}
constexpr bool IsPositive() const {
return total_milliseconds_ > Milliseconds::zero();
}
constexpr bool IsNonNegative() const {
return total_milliseconds_ >= Milliseconds::zero();
}
// Basic accessor and conversions (rounds down).
constexpr Milliseconds total_milliseconds() const {
return total_milliseconds_;
}
constexpr Seconds total_seconds() const {
return DurationTruncate<Seconds>(total_milliseconds_);
}
constexpr Minutes total_minutes() const {
return DurationTruncate<Minutes>(total_milliseconds_);
}
constexpr Hours total_hours() const {
return DurationTruncate<Hours>(total_milliseconds_);
}
constexpr Days total_days() const {
return DurationTruncate<Days>(total_milliseconds_);
}
// Access duration absolute components bounded within a
// particular unit.
// Milliseconds modulo milliseconds per-second (0-999)
constexpr uint32_t milliseconds() const {
if (IsNegative()) return GetAbsolute().milliseconds();
return static_cast<uint32_t>(
(total_milliseconds() - total_seconds()).count());
}
// Seconds modulo seconds per-minute (0-59)
constexpr uint32_t seconds() const {
if (IsNegative()) return GetAbsolute().seconds();
return static_cast<uint32_t>((total_seconds() - total_minutes()).count());
}
// Minutes modulo minutes per-hour (0-59).
constexpr uint32_t minutes() const {
if (IsNegative()) return GetAbsolute().minutes();
return static_cast<uint32_t>((total_minutes() - total_hours()).count());
}
// Hours modulo hours per-day (0-23)
constexpr uint32_t hours() const {
if (IsNegative()) return GetAbsolute().hours();
return static_cast<uint32_t>((total_hours() - total_days()).count());
}
// Days (total).
constexpr uint32_t days() const {
if (IsNegative()) return GetAbsolute().days();
return static_cast<uint32_t>(total_days().count());
}
// Obtain the absolute value.
constexpr Duration GetAbsolute() const {
return Duration(std::chrono::abs(total_milliseconds_));
}
// Obtain the truncated (math term for rounding towards zero)
// value by units.
constexpr Duration GetTruncateBySeconds() const {
return GetTruncateInternal<Seconds>();
}
constexpr Duration GetTruncateByMinutes() const {
return GetTruncateInternal<Minutes>();
}
constexpr Duration GetTruncateByHours() const {
return GetTruncateInternal<Hours>();
}
constexpr Duration GetTruncateByDays() const {
return GetTruncateInternal<Days>();
}
// Comparison operators (between another Duration class)
constexpr bool IsEqualTo(const Duration& other) const {
return total_milliseconds_ == other.total_milliseconds_;
}
constexpr int64_t CompareTo(const Duration& other) const {
return static_cast<int64_t>(total_milliseconds_.count() -
other.total_milliseconds_.count());
}
WVCDM_DEFINE_CONSTEXPR_EQ_AND_CMP_OPERATORS(Duration);
// Comparison operators (between other duration types)
template <class Rep, class Period>
constexpr bool operator==(
const std::chrono::duration<Rep, Period>& other) const {
return total_milliseconds_ == other;
}
template <class Rep, class Period>
constexpr bool operator!=(
const std::chrono::duration<Rep, Period>& other) const {
return total_milliseconds_ != other;
}
template <class Rep, class Period>
constexpr bool operator<=(
const std::chrono::duration<Rep, Period>& other) const {
return total_milliseconds_ <= other;
}
template <class Rep, class Period>
constexpr bool operator<(
const std::chrono::duration<Rep, Period>& other) const {
return total_milliseconds_ < other;
}
template <class Rep, class Period>
constexpr bool operator>=(
const std::chrono::duration<Rep, Period>& other) const {
return total_milliseconds_ >= other;
}
template <class Rep, class Period>
constexpr bool operator>(
const std::chrono::duration<Rep, Period>& other) const {
return total_milliseconds_ > other;
}
// Unary plus/minus operators
constexpr Duration operator+() const { return Duration(total_milliseconds_); }
constexpr Duration operator-() const {
return Duration(-total_milliseconds_);
}
// Add/subtract operators (between another Duration class)
constexpr Duration operator+(const Duration& other) const {
return Duration(total_milliseconds_ + other.total_milliseconds_);
}
constexpr Duration operator-(const Duration& other) const {
return Duration(total_milliseconds_ - other.total_milliseconds_);
}
// Add/subtract operators (between other duration types)
// Note 1: This is intentionally implemented as a member function to
// force the caller the put the Duration class first in the
// expression.
// Note 2: These operators are not defined for duration types that
// smaller than chrono::milliseconds. This could cause
// unexpected behavior as operations would be truncated.
// Ex: (Duration(5s) + 500us + 500us) = Duration(5s)
// Compiler will not allow such operations.
template <class Rep, class Period>
constexpr Duration operator+(
const std::chrono::duration<Rep, Period>& other) const {
return Duration(total_milliseconds_ + other);
}
template <class Rep, class Period>
constexpr Duration operator-(
const std::chrono::duration<Rep, Period>& other) const {
return Duration(total_milliseconds_ - other);
}
// Increment/decrement operators (between another Duration class)
constexpr Duration& operator+=(const Duration& other) {
total_milliseconds_ += other.total_milliseconds_;
return *this;
}
constexpr Duration& operator-=(const Duration& other) {
total_milliseconds_ -= other.total_milliseconds_;
return *this;
}
// Increment/decrement operators (between another duration types)
// Note: These operators are not defined for duration types that
// smaller than chrono::milliseconds (see '+' operator
// comment for details).
template <class Rep, class Period>
constexpr Duration& operator+=(
const std::chrono::duration<Rep, Period>& other) {
total_milliseconds_ += other;
return *this;
}
template <class Rep, class Period>
constexpr Duration& operator-=(
const std::chrono::duration<Rep, Period>& other) {
total_milliseconds_ -= other;
return *this;
}
// To string operator.
// Converts the Duration to its string representation.
// Examples:
// Duration::FromMilliseconds(4000123).ToString()
// ==> "1h6m40s123ms"
// Duration::FromSeconds(4000).ToString()
// ==> "1h6m40s"
bool PrintTo(std::ostream* out) const;
std::string ToString() const;
private:
// Internally, the Duration class stores durations in milliseconds.
Milliseconds total_milliseconds_ = Milliseconds::zero();
}; // class Duration
// == GTest Printer ==
void PrintTo(const Duration& duration, std::ostream* out);
} // namespace wvutil
#endif // WVCDM_UTIL_WV_DURATION_H_