#ifndef OPENVPN_TIME_TIME_H #define OPENVPN_TIME_TIME_H #include #include #include #ifdef OPENVPN_PLATFORM_WIN #include // for ::time() on Windows #include // for GetTickCount #else #include // for ::time() and ::gettimeofday() #endif namespace openvpn { OPENVPN_SIMPLE_EXCEPTION(get_time_error); template class TimeType { public: enum { prec=1024 }; typedef ::time_t base_type; typedef T type; class Duration { friend class TimeType; public: static Duration seconds(const T v) { return Duration(v * prec); } static Duration binary_ms(const T v) { return Duration(v); } static Duration infinite() { return Duration(std::numeric_limits::max()); } Duration() : duration_(T(0)) {} bool defined() const { return duration_ != T(0); } bool operator!() const { return duration_ == T(0); } bool is_infinite() const { return duration_ == std::numeric_limits::max(); } void set_infinite() { duration_ = std::numeric_limits::max(); } void set_zero() { duration_ = T(0); } Duration operator+(const Duration& d) const { if (is_infinite() || d.is_infinite()) return infinite(); else return Duration(duration_ + d.duration_); } Duration& operator+=(const Duration& d) { if (is_infinite() || d.is_infinite()) set_infinite(); else duration_ += d.duration_; return *this; } void min(const Duration& d) { if (d.duration_ < duration_) duration_ = d.duration_; } void max(const Duration& d) { if (d.duration_ > duration_) duration_ = d.duration_; } Duration operator-(const Duration& d) const { if (d.duration_ >= duration_) return Duration(0); else if (is_infinite()) return Duration::infinite(); else return Duration(duration_ - d.duration_); } Duration& operator-=(const Duration& d) { if (d.duration_ >= duration_) set_zero(); else if (!is_infinite()) duration_ -= d.duration_; return *this; } T to_seconds() const { return duration_ / prec; } T to_binary_ms() const { return duration_; } T to_milliseconds() const { return duration_ - (duration_ * T(3) / T(128)); // NOTE: assumes that prec == 1024 } T raw() const { return duration_; } # define OPENVPN_DURATION_REL(OP) bool operator OP(const Duration& d) const { return duration_ OP d.duration_; } OPENVPN_DURATION_REL(==) OPENVPN_DURATION_REL(!=) OPENVPN_DURATION_REL(>) OPENVPN_DURATION_REL(<) OPENVPN_DURATION_REL(>=) OPENVPN_DURATION_REL(<=) # undef OPENVPN_DURATION_REL private: explicit Duration(const T duration) : duration_(duration) {} T duration_; }; TimeType() : time_(T(0)) {} static TimeType zero() { return TimeType(T(0)); } static TimeType infinite() { return TimeType(std::numeric_limits::max()); } bool is_infinite() const { return time_ == std::numeric_limits::max(); } void reset() { time_ = 0; } void set_infinite() { time_ = std::numeric_limits::max(); } bool defined() const { return time_ != 0; } bool operator!() const { return time_ == 0; } base_type seconds_since_epoch() const { return base_ + time_ / prec; } T fractional_binary_ms() const { return time_ % prec; } static TimeType now() { return TimeType(now_()); } void update() { time_ = now_(); } TimeType operator+(const Duration& d) const { if (is_infinite() || d.is_infinite()) return infinite(); else return TimeType(time_ + d.duration_); } TimeType& operator+=(const Duration& d) { if (is_infinite() || d.is_infinite()) set_infinite(); else time_ += d.duration_; return *this; } Duration operator-(const TimeType& t) const { if (t.time_ >= time_) return Duration(0); else if (is_infinite()) return Duration::infinite(); else return Duration(time_ - t.time_); } void min(const TimeType& t) { if (t.time_ < time_) time_ = t.time_; } void max(const TimeType& t) { if (t.time_ > time_) time_ = t.time_; } # define OPENVPN_TIME_REL(OP) bool operator OP(const TimeType& t) const { return time_ OP t.time_; } OPENVPN_TIME_REL(==) OPENVPN_TIME_REL(!=) OPENVPN_TIME_REL(>) OPENVPN_TIME_REL(<) OPENVPN_TIME_REL(>=) OPENVPN_TIME_REL(<=) # undef OPENVPN_TIME_REL T raw() const { return time_; } static void reset_base() { base_ = ::time(0); # ifdef OPENVPN_PLATFORM_WIN win_recalibrate(::GetTickCount()); # endif } private: explicit TimeType(const T time) : time_(time) {} #ifdef OPENVPN_PLATFORM_WIN static void win_recalibrate(const DWORD gtc) { gtc_last = gtc; gtc_base = ::time(0) - gtc_last/1000; } static T now_() { const DWORD gtc = ::GetTickCount(); if (gtc < gtc_last) win_recalibrate(gtc); const time_t sec = gtc_base + gtc / 1000; const unsigned int msec = gtc % 1000; return T((sec - base_) * prec + msec * prec / 1000); } static DWORD gtc_last; static time_t gtc_base; #else static T now_() { ::timeval tv; if (::gettimeofday(&tv, NULL) != 0) throw get_time_error(); return T((tv.tv_sec - base_) * prec + tv.tv_usec * prec / 1000000); } #endif static base_type base_; T time_; }; #ifdef OPENVPN_PLATFORM_WIN template DWORD TimeType::gtc_last; template time_t TimeType::gtc_base; #endif template typename TimeType::base_type TimeType::base_; typedef TimeType Time; typedef Time* TimePtr; } // namespace openvpn #endif // OPENVPN_TIME_TIME_H