implement basic operations over class Time
[libi2ncommon] / src / timefunc.hxx
index eec351b..09e4e5c 100644 (file)
@@ -26,14 +26,27 @@ on this file might be covered by the GNU General Public License.
 #ifndef __TIMEFUNC_HXX
 #define __TIMEFUNC_HXX
 
+#include <climits>
 #include <errno.h>
-#include <string>
 #include <list>
+#include <string>
 
 #include <boost/optional.hpp>
 
 #include <week.hpp>
 
+#include "stringfunc.hxx"
+
+#if __cplusplus >= 201103
+# define CONSTEXPR constexpr
+# define NOEXCEPT  noexcept
+#else
+# define CONSTEXPR
+# define NOEXCEPT
+#endif
+
+#define TIME_CONST_FACTOR_NANO (1000L * 1000 * 1000)
+
 double prec_time(void);
 
 time_t date_to_seconds(const std::string &date);
@@ -220,6 +233,19 @@ namespace I2n {
 
 namespace clock {
 
+    namespace {
+
+        /* helper for ctor initializer list; we still lack aggregate initializers */
+        struct timespec
+        timespec_of_parts (const time_t sec, const long nsec)
+        {
+            struct timespec ret = { sec, nsec};
+
+            return ret;
+        }
+
+    } /* [namespace] */
+
     namespace type {
         /*
          * represent the clock id options from clock_gettime(2) as
@@ -251,58 +277,274 @@ namespace clock {
     class Time {
 
         private:
-            struct timespec           value;
+            struct timespec     value;
 
         public:
-            const enum type::id       id;
-            const enum type::variant  variant;
-            int                       err;
+            enum type::id       id;
+            enum type::variant  variant;
+            int                 err;
+
+        private:
+            /*
+             * Handle decimal part (nanosecond) overflow; this is performed
+             * after arithmetic operations and whenever we there is the
+             * possibility of unsanitized input for example in constructors
+             * from other types and suchlike.
+             *
+             * POSIX defines the ns part as *long*. Technically, that means
+             * that on machines where *sizeof long* equals *sizeof int*, it can
+             * represent only up to arund 2.1 seconds. In this range, the loop
+             * version is most likely faster than division. However, since in
+             * practice *long* is 8 bytes just about anywhere, we have to
+             * handle greater dividends first.
+             */
+            inline void
+            carry_nsec (void)
+            {
+#           if LONG_BIT > 32
+                if (   this->value.tv_nsec < -3L * TIME_CONST_FACTOR_NANO
+                    || this->value.tv_nsec >  3L * TIME_CONST_FACTOR_NANO)
+                {
+                    const long sec = this->value.tv_nsec / TIME_CONST_FACTOR_NANO;
+                    this->value.tv_nsec -= sec * TIME_CONST_FACTOR_NANO;
+                    this->value.tv_sec  += sec;
+                }
+#           endif /* [LONG_BIT > 32] */
+                while (this->value.tv_nsec >= TIME_CONST_FACTOR_NANO) {
+                    this->value.tv_sec  += 1;
+                    this->value.tv_nsec -= TIME_CONST_FACTOR_NANO;
+                }
+
+                while (this->value.tv_nsec < 0) {
+                    this->value.tv_sec  -= 1;
+                    this->value.tv_nsec += TIME_CONST_FACTOR_NANO;
+                }
+            }
 
         /* ctors *************************************************************/
         public:
 
             Time (const enum type::id      id  = type::mono,
-                  const enum type::variant var = type::dflt);
-
-        /* value access ******************************************************/
+                  const enum type::variant var = type::dflt) NOEXCEPT;
+
+            inline Time (const Time &t) NOEXCEPT
+                : value   (t.value)
+                , id      (t.id)
+                , variant (t.variant)
+                , err     (t.err)
+            { }
+
+            inline Time (const time_t sec,
+                         const long               nsec = 0,
+                         const enum type::id      id   = type::mono,
+                         const enum type::variant var  = type::dflt,
+                         const int                err  = 0) NOEXCEPT
+                : value   (timespec_of_parts (sec, nsec))
+                , id      (id)
+                , variant (var)
+                , err     (err)
+            { this->carry_nsec (); }
+
+        /* value read access *************************************************/
         public:
 
-            inline const struct timespec &get_time (void)
+            inline CONSTEXPR const struct timespec &get_time (void) const NOEXCEPT
             { return this->value; }
 
-            inline const time_t &get_sec (void)
-            { return this->value.tv_sec; };
+            inline CONSTEXPR const time_t &get_sec (void) const NOEXCEPT
+            { return this->value.tv_sec; }
 
-            inline const long &get_nsec (void)
+            inline CONSTEXPR const long &get_nsec (void) const NOEXCEPT
             { return this->value.tv_nsec; }
 
-            int64_t as_nanosec (void);
+            int64_t as_nanosec (void) const NOEXCEPT;
 
-            long as_nanosec_L (void);
+            long as_nanosec_L (void) const NOEXCEPT;
 
-        /* setting time ******************************************************/
+        /* value write access ************************************************/
         public:
 
             inline void
-            set (const struct timespec &ts)
-            { this->value = ts; };
+            swap (Time &t) NOEXCEPT
+            {
+                std::swap (this->value  , t.value  );
+                std::swap (this->id     , t.id     );
+                std::swap (this->variant, t.variant);
+                std::swap (this->err    , t.err    );
+            }
+
+            Time &operator= (Time t) NOEXCEPT;
+
+            Time &operator= (struct timespec ts) NOEXCEPT;
 
             inline void
-            set (const time_t sec, const long nsec)
+            set (const struct timespec &ts) NOEXCEPT
+            {
+                this->value = ts;
+                this->carry_nsec ();
+            }
+
+            inline void
+            set (const time_t sec,
+                 const long   nsec,
+                 const enum type::id      id  = type::mono,
+                 const enum type::variant var = type::dflt) NOEXCEPT
             {
                 this->value.tv_sec  = sec;
                 this->value.tv_nsec = nsec;
-            };
+                this->id      = id;
+                this->variant = var;
+
+                this->carry_nsec ();
+            }
 
-            bool set (void);
+            bool set (void) NOEXCEPT;
+
+            void unset (void) NOEXCEPT;
+
+        /* arithmetic ********************************************************/
+        public:
 
-            void unset (void);
+            Time &add (const time_t sec, const long nsec) NOEXCEPT;
+
+            Time &subtract (const time_t sec, const long nsec) NOEXCEPT;
+
+            inline Time &add (const Time &t2) NOEXCEPT
+            { return this->add (t2.value.tv_sec, t2.value.tv_nsec); }
+
+            inline Time &add (const time_t t2) NOEXCEPT
+            { return this->add (t2, 0L); };
+
+            inline Time &subtract (const Time &t2) NOEXCEPT
+            { return this->subtract (t2.value.tv_sec, t2.value.tv_nsec); }
+
+            inline Time &subtract (const time_t t2) NOEXCEPT
+            { return this->subtract (t2, 0L); };
+
+            Time &scale (const time_t factor) NOEXCEPT;
+
+            friend int compare (const Time &t1, const Time &t2) NOEXCEPT;
+
+            inline Time
+            difference (const Time &t) NOEXCEPT
+            { return (*this < t) ? t - *this : *this - t; }
+
+        /* overloads *********************************************************/
+        public:
+
+            inline Time
+            operator+ (const Time &t2) const NOEXCEPT
+            { return Time (*this).add (t2); }
+
+            inline Time
+            operator+ (const time_t t2) const NOEXCEPT
+            { return Time (*this).add (t2); }
+
+            inline Time &
+            operator+= (const Time &t2) NOEXCEPT
+            { return this->add (t2); }
+
+            inline Time &
+            operator+= (const time_t t2) NOEXCEPT
+            { return this->add (t2); }
+
+            inline Time
+            operator- (const Time &t2) const NOEXCEPT
+            { return Time (*this).subtract (t2); }
+
+            inline Time
+            operator- (const time_t t2) const NOEXCEPT
+            { return Time (*this).subtract (t2); }
+
+            inline Time &
+            operator-= (const Time &t2) NOEXCEPT
+            { return this->subtract (t2); }
+
+            inline Time &
+            operator-= (const time_t t2) NOEXCEPT
+            { return this->subtract (t2); }
+
+            inline Time
+            operator* (const time_t factor) const NOEXCEPT
+            { return Time (*this).scale (factor); }
+
+            inline Time &
+            operator*= (const time_t factor) NOEXCEPT
+            { return this->scale (factor); }
+
+            friend CONSTEXPR bool
+            operator== (const Time &t1, const Time &t2) NOEXCEPT;
+
+            friend CONSTEXPR bool
+            operator< (const Time &t1, const Time &t2) NOEXCEPT;
+
+            friend CONSTEXPR bool
+            operator> (const Time &t1, const Time &t2) NOEXCEPT;
+
+            friend std::ostream &
+            operator<< (std::ostream &os, const Time &t);
 
     }; /* [class Time] */
 
+    inline Time 
+    operator+ (const time_t t1, const Time &t2) NOEXCEPT
+    { return Time (t1) + t2; }
+
+    inline Time 
+    operator- (const time_t t1, const Time &t2) NOEXCEPT
+    { return Time (t1) - t2; }
+
+    int compare (const Time &t1, const Time &t2) NOEXCEPT;
+
+    /*
+     * comparison for equality also considers the clock type;
+     */
+    inline CONSTEXPR bool
+    operator== (const Time &t1, const Time &t2) NOEXCEPT
+    {
+        return t1.id == t2.id && t1.variant == t2.variant
+            && t1.value.tv_sec  == t2.value.tv_sec
+            && t1.value.tv_nsec == t2.value.tv_nsec
+            ;
+    }
+
+    /* these ignore the *id* and *variant* fields */
+    inline CONSTEXPR bool
+    operator< (const Time &t1, const Time &t2) NOEXCEPT
+    {
+        return t1.value.tv_sec < t2.value.tv_sec
+            || (   t1.value.tv_sec  == t2.value.tv_sec
+                && t1.value.tv_nsec <  t2.value.tv_nsec)
+            ;
+    }
+
+    /* these ignore the *id* and *variant* fields */
+    inline CONSTEXPR bool
+    operator> (const Time &t1, const Time &t2) NOEXCEPT
+    {
+        return t1.value.tv_sec > t2.value.tv_sec
+            || (   t1.value.tv_sec  == t2.value.tv_sec
+                && t1.value.tv_nsec >  t2.value.tv_nsec)
+            ;
+    }
+
+    inline std::ostream &
+    operator<< (std::ostream &os, const Time &t)
+    {
+        os << I2n::to_string (t.value.tv_sec)  << "ms, "
+           << I2n::to_string (t.value.tv_nsec) << "ns"
+           ;
+
+        return os;
+    }
+
     boost::optional<Time>
     now (const enum type::id      id  = type::mono,
-         const enum type::variant var = type::dflt);
+         const enum type::variant var = type::dflt) NOEXCEPT;
+    Time
+    zero (const enum type::id      id  = type::mono,
+          const enum type::variant var = type::dflt) NOEXCEPT;
 
 } /* [namespace clock] */