X-Git-Url: http://rtime.felk.cvut.cz/gitweb/frescor/fosa.git/blobdiff_plain/c8948702a2abdf14f152d9776ab91e3e6283e045..29660bd13b283860b273f7c3d310cd6e696efe1d:/include/fosa_time_timespec.h diff --git a/include/fosa_time_timespec.h b/include/fosa_time_timespec.h index 4d5f2b5..942d9e5 100644 --- a/include/fosa_time_timespec.h +++ b/include/fosa_time_timespec.h @@ -65,23 +65,33 @@ #ifndef FOSA_TIME_TIMESPEC_H_ #define FOSA_TIME_TIMESPEC_H_ +#include +#include /**********************************/ /* 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_ */