]> rtime.felk.cvut.cz Git - frescor/fosa.git/blobdiff - include/fosa_time_timespec.h
Additions to fosa time to be used with FRSH
[frescor/fosa.git] / include / fosa_time_timespec.h
index 4d5f2b5b26e2c636032272e567d8618fba6f273f..942d9e5515f1f0246557aff3f21ea69a19216af3 100644 (file)
 #ifndef        FOSA_TIME_TIMESPEC_H_
 #define        FOSA_TIME_TIMESPEC_H_
 
+#include <stdlib.h>
+#include <assert.h>
 
 /**********************************/
 /* T I M E S P E C   M A C R O S  */
 /**********************************/
 
+#define normalize_timespec(t1) \
+do \
+{ \
+    if ((t1).tv_nsec >= 1000000000) \
+    { \
+        (t1).tv_sec++; \
+        (t1).tv_nsec -= 1000000000; \
+    } \
+} while(0)
+
+
 #define add_timespec(sum, t1, t2) \
 do { \
     (sum).tv_sec = (t1).tv_sec + (t2).tv_sec; \
     (sum).tv_nsec = (t1).tv_nsec + (t2).tv_nsec; \
-    if ((sum).tv_nsec >= 1000000000) \
-    { \
-        (sum).tv_sec++; \
-        (sum).tv_nsec -= 1000000000; \
-    } \
+    normalize_timespec(sum); \
 } while(0)
 
 
+
 // -------------------------------------------------------------------
 
 /* TODO:  Test that for past > future we obtain a correct negative
@@ -149,7 +159,34 @@ do { \
    ( ((t1).tv_sec % 2148) * 1000000 + (t1).tv_nsec/1000 )
 
 
+// ---------------------------------------------------------
+
+#define nsec_to_timespec(tspec, nsec) \
+do { \
+    if ((nsec) >= 1000000000) { \
+        (tspec).tv_sec = (nsec)/1000000000; \
+        (tspec).tv_nsec = (nsec) % 1000000000; \
+    } else { \
+        (tspec).tv_sec = 0; \
+        (tspec).tv_nsec = (nsec); \
+    } \
+} while(0)
+
+// ---------------------------------------------------------
+
+#define timespec_to_nsec(t1) \
+   ( ((t1).tv_sec % 2) * 1000000000 + (t1).tv_nsec )
+
+
+
+// ---------------------------------------------------------
+
+#define timespec_equal(t1, t2) ( (t1).tv_sec == (t2).tv_sec ) && ( (t1).tv_nsec == (t2).tv_nsec )
+
+#define timespec_is_null(t1) ( (t1).tv_sec == 0 ) && ( (t1).tv_nsec == 0 )
 
+
+  
 /***************************************/
 /* T I M E S P E C   F U N C T I O N S */
 /***************************************/
@@ -171,8 +208,6 @@ static inline fosa_abs_time_t fosa_abs_time_decr(fosa_abs_time_t base, fosa_rel_
 {
     fosa_abs_time_t result;
 
-
-
     substract_timespec(result, base, interval);
 
     return result;
@@ -204,7 +239,6 @@ static inline fosa_rel_time_t fosa_rel_time_add(fosa_rel_time_t relt1, fosa_rel_
 }
     
 
-
 // ---------------------------------------------------------
 
 static inline fosa_rel_time_t fosa_rel_time_decr(fosa_rel_time_t total, fosa_rel_time_t part)
@@ -216,6 +250,224 @@ static inline fosa_rel_time_t fosa_rel_time_decr(fosa_rel_time_t total, fosa_rel
     return result;
 }
 
+// ---------------------------------------------------------
+
+/**
+ * exact_long_multiply_smaller_10e5()
+ *
+ * Same as below but with operands smaller than 10000 which allows
+ * to perform all component multiplications without an overflow risk. 
+ **/
+static inline struct timespec exact_long_multiply_smaller_10e5(long t, long k)
+{
+    /* Since operands are smaller than 10e5 we can use 1000 */
+    /* the base without risking to cause overflows in the   */
+    /* operations.                                          */
+    /********************************************************/
+    assert(t < 10000);
+    assert(k < 10000);
+
+    struct timespec result;
+
+    long base = 1000;
+
+    long t_high = t / base;
+    long t_low = t % base;
+    long k_high = k / base;
+    long k_low = k % base;
+
+    long thkh = t_high * k_high;
+    long thkl = t_high * k_low;
+    long tlkh = t_low * k_high;
+    long tlkl = t_low * k_low;
+
+    long thkl_high = thkl / base;
+    long tlkh_high = tlkh / base;
+    long thkl_low = thkl % base;
+    long tlkh_low = tlkh % base;
+
+    long c2 = thkh + thkl_high + tlkh_high; // Normalized at 10^6
+    long c1 = thkl_low + tlkh_low;  // Normalized at 10^3
+    long c0 = tlkl;
+
+    result.tv_sec = c2/1000 + c1/1000000;
+    result.tv_nsec = (c2 % 1000) * 1000000 + (c1 % 1000000) * 1000 + c0;
+
+    result.tv_sec += (result.tv_nsec / 1000000000);
+    result.tv_nsec %= 1000000000;
+
+    return result;
+}
+
+    
+// -------------------------------------------------------------    
+
+    
+
+/**
+ * exact_long_multiply()
+ *
+ * This function performs an exact long * long operations on operands
+ * lower than 1e9 without using more than 32bit (1e9) arithmetic.
+ *
+ * To achieve this we decompose the operands in high and low:
+ *
+ * num = (num/base) * base + (num % base)
+ *        ^^^^^^^^           ^^^^^^^^^^^^
+ *        high component     low component
+ *
+ * t * k = (th*kh + th*kl/base + tl*kh/base  )*base^2
+ *         +  (th*kl % base)*base + (tl*kh % base) * base + tl*kl
+ *
+ * The problem is that we cannot use an exact base because sqrt(1e9)
+ * is not a multiple of 1e9 (in fact it is not even integer).
+ *
+ * So we use as a base 100000 which means that the last term tl*kl may
+ * need another exact calculation with a lower base (100)
+ **/
+static inline struct timespec exact_long_multiply(long t, long k)
+{
+    struct timespec result;
+
+    long t_high;
+    long t_low;
+    long k_high;
+    long k_low;
+
+    long base = 100000;  /* Power of 10 closest to sqrt(1e9) from
+                          * above */
+
+    t_high = t / base;
+    t_low = t % base;  // Between 0 99999
+    k_high = k / base;
+    k_low = k % base;  // Between 0 99999
+
+    /* These numbers are always lower than 1e9 */
+    long thkh = t_high * k_high;
+    long thkl = t_high * k_low;
+    long tlkh = t_low * k_high;
+
+    long thkl_high = thkl / base;
+    long thkl_low = thkl % base;
+    long tlkh_high = tlkh / base;
+    long tlkh_low = tlkh % base;
+
+    /* We can calculate the base^2 term (note however that this is 10
+     * times the tv_sec component because it is multiplied by 10^10 */
+    long c2 = thkh + thkl_high + tlkh_high;  // scaled to 10^10
+    long c1 = thkl_low + tlkh_low;  // scaled to 10^5
+
+    struct timespec c0;
+
+    /* Now we can write the initial values of result */
+    result.tv_sec = c2*10 + c1/10000;
+    result.tv_nsec = (c1 % 10000) * 100000;
+
+    normalize_timespec(result);
+
+    /* To calculate c0 we must check if there is a risk of overflow */
+    /* Remember operands cannot be larger than 99999 and result     */
+    /* larger than 1e9.                                             */
+    /****************************************************************/
+    bool overflow_risk;
+    overflow_risk = false;
+
+    if (
+        ( (t_low > 31622) && (k_low >= 10000) ) ||
+        ( (k_low > 31622) && (t_low >= 10000) )
+        )
+    {
+        overflow_risk = true;
+    }
+    
+    if (! overflow_risk)
+    {
+        c0.tv_sec = 0;
+        c0.tv_nsec = t_low * k_low;
+        normalize_timespec(c0);
+    }
+    else
+    {
+        c0 = exact_long_multiply_smaller_10e5(t_low, k_low);
+    }
+    add_timespec(result, result, c0);
+
+    return result;
+}
+    
+// -------------------------------------------------------------    
+
+
+static inline fosa_rel_time_t fosa_rel_time_times_integer(fosa_rel_time_t time, long multiplier)
+{
+    struct timespec result;
+    struct timespec intermediate;
+
+    result.tv_sec = time.tv_sec * multiplier;  // No overflow check here
+    result.tv_nsec = 0;
+
+    intermediate = exact_long_multiply(time.tv_nsec, multiplier);
+
+    add_timespec(result, result, intermediate);
+    
+    return result;
+}
+
+// ---------------------------------------------------------
+
+static inline fosa_rel_time_t fosa_rel_time_divided_by_integer(fosa_rel_time_t time, long divider)
+{
+    struct timespec result;
+    long reminder;
+
+
+    result.tv_sec = time.tv_sec / divider;
+    result.tv_nsec = 0;
+
+    /* We iterate until the reminder is lower than 2 (to later fit in
+     * a 32 bit signed integer multiplied by 1e9 */
+
+    reminder = time.tv_sec % divider;
+
+
+    if (reminder == 0)
+    {
+        /* We are lucky no reminder so no need to transfer it to the
+           nsec scale.
+        */
+        result.tv_nsec = time.tv_nsec/divider;
+        return result;
+    }
+
+    long enhacer;
+    long back_enhacer;
+    long next_dividend;
+    int next_numeral; /* Between 0 and 9 */
+    enhacer = 1;
+    do
+    {
+        enhacer *= 10;
+        back_enhacer = 1000000000 / enhacer;
+        next_numeral = (time.tv_nsec / back_enhacer) % 10;
+        
+        // Note:  a possible overflow may happen here with large denominators
+        if (reminder > 200000000)
+        {
+            /* An overflow is going to be produced */
+            abort();
+        }
+        next_dividend = reminder * 10 + next_numeral;
+        
+        result.tv_nsec += (next_dividend / divider) * back_enhacer;
+        reminder = next_dividend % divider;
+    } while (back_enhacer > 1);
+
+    
+    return result;
+}
+
+// ---------------------------------------------------------
+
 
 /* Comparison */
 /**************/
@@ -264,6 +516,55 @@ static inline bool fosa_rel_time_smaller_or_equal(fosa_rel_time_t relt1, fosa_re
 }
 
 
+// -----------------------------------------------------------
+
+static inline bool fosa_rel_time_equal(fosa_rel_time_t relt1, fosa_rel_time_t relt2)
+{
+    bool result;
+
+    result = timespec_equal(relt1, relt2);
+
+    return result;
+}
+
+
+// -----------------------------------------------------------
+
+static inline bool fosa_abs_time_equal(fosa_abs_time_t abst1, fosa_abs_time_t abst2)
+{
+    bool result;
+
+    result = timespec_equal(abst1, abst2);
+
+    return result;
+}
+
+// -----------------------------------------------------------
+
+static inline bool fosa_rel_time_is_null(fosa_rel_time_t relt)
+{
+    bool result;
+
+    result = timespec_is_null(relt);
+
+    return result;
+}
+
+
+// -----------------------------------------------------------
+
+static inline bool fosa_abs_time_is_null(fosa_abs_time_t abst)
+{
+    bool result;
+
+    result = timespec_is_null(abst);
+
+    return result;
+}
+
+
+
+
 /* Conversion */
 /**************/
 static inline fosa_rel_time_t fosa_msec_to_rel_time(long msec)
@@ -358,6 +659,51 @@ static inline long fosa_abs_time_to_usec(fosa_abs_time_t abst)
     return result;
 }
 
+// --------------------------------------------------
+
+static inline fosa_rel_time_t fosa_nsec_to_rel_time(long nsec)
+{
+    fosa_rel_time_t result;
+
+    nsec_to_timespec(result, nsec);
+
+    return result;
+}
+
+// --------------------------------------------------
+
+static inline long fosa_rel_time_to_nsec(fosa_rel_time_t relt)
+{
+    long result;
+
+    result = timespec_to_nsec(relt);
+
+    return result;
+}
+
+
+// --------------------------------------------------
+
+static inline fosa_abs_time_t fosa_nsec_to_abs_time(long nsec)
+{
+    fosa_abs_time_t result;
+
+    nsec_to_timespec(result, nsec);
+
+    return result;
+}
+
+// --------------------------------------------------
+
+static inline long fosa_abs_time_to_nsec(fosa_abs_time_t abst)
+{
+    long result;
+
+    result = timespec_to_nsec(abst);
+
+    return result;
+}
+
 
 // --------------------------------------------------
 
@@ -387,5 +733,27 @@ static inline struct timespec fosa_abs_time_to_timespec(fosa_abs_time_t abst)
     return (struct timespec) abst;
 }
 
+// --------------------------------------------------
+
+static inline double fosa_rel_time_to_double(fosa_rel_time_t relt)
+{
+    double result;
+
+    result = relt.tv_nsec*0.000000001 + (double)relt.tv_sec;
+
+    return result;
+}
+
+// --------------------------------------------------
+
+static inline fosa_rel_time_t fosa_double_to_rel_time(double time)
+{
+    fosa_rel_time_t result;
+
+    result.tv_sec = (long) time;
+    result.tv_nsec = (long) ( (time - (double)result.tv_sec) * 1e9 );
+
+    return result;
+}
 
 #endif             /* !FOSA_TIME_TIMESPEC_H_ */