summaryrefslogtreecommitdiff
path: root/ccan/time/time.c
blob: d0542f5654e86f06e69bc40c530ce0c989643e92 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
/* Licensed under BSD-MIT - see LICENSE file for details */
#include <ccan/time/time.h>
#include <stdlib.h>
#include <stdio.h>

#if !HAVE_CLOCK_GETTIME
#include <sys/time.h>

struct timeabs time_now(void)
{
	struct timeval now;
	struct timeabs ret;
	gettimeofday(&now, NULL);
	ret.ts.tv_sec = now.tv_sec;
	ret.ts.tv_nsec = now.tv_usec * 1000;
	return TIMEABS_CHECK(ret);
}
#else
#include <time.h>
struct timeabs time_now(void)
{
	struct timeabs ret;
	clock_gettime(CLOCK_REALTIME, &ret.ts);
	return TIMEABS_CHECK(ret);
}
#endif /* HAVE_CLOCK_GETTIME */

struct timemono time_mono(void)
{
	struct timemono ret;
#ifdef TIME_HAVE_MONOTONIC
	clock_gettime(CLOCK_MONOTONIC, &ret.ts);
#else /* Best we can do */
	ret.ts = time_now().ts;
#endif /* !HAVE_TIME_MONOTONIC */
	return ret;
}

struct timerel time_divide(struct timerel t, unsigned long div)
{
	struct timerel res;
	uint64_t rem, ns;

	/* Dividing seconds is simple. */
	res.ts.tv_sec = TIMEREL_CHECK(t).ts.tv_sec / div;
	rem = t.ts.tv_sec % div;

	/* If we can't fit remainder * 1,000,000,000 in 64 bits? */
#if 0 /* ilog is great, but we use fp for multiply anyway. */
	bits = ilog64(rem);
	if (bits + 30 >= 64) {
		/* Reduce accuracy slightly */
		rem >>= (bits - (64 - 30));
		div >>= (bits - (64 - 30));
	}
#endif
	if (rem & ~(((uint64_t)1 << 30) - 1)) {
		/* FIXME: fp is cheating! */
		double nsec = rem * 1000000000.0 + t.ts.tv_nsec;
		res.ts.tv_nsec = nsec / div;
	} else {
		ns = rem * 1000000000 + t.ts.tv_nsec;
		res.ts.tv_nsec = ns / div;
	}
	return TIMEREL_CHECK(res);
}

struct timerel time_multiply(struct timerel t, unsigned long mult)
{
	struct timerel res;

	/* Are we going to overflow if we multiply nsec? */
	if (mult & ~((1UL << 30) - 1)) {
		/* FIXME: fp is cheating! */
		double nsec = (double)t.ts.tv_nsec * mult;

		res.ts.tv_sec = nsec / 1000000000.0;
		res.ts.tv_nsec = nsec - (res.ts.tv_sec * 1000000000.0);
	} else {
		uint64_t nsec = t.ts.tv_nsec * mult;

		res.ts.tv_nsec = nsec % 1000000000;
		res.ts.tv_sec = nsec / 1000000000;
	}
	res.ts.tv_sec += TIMEREL_CHECK(t).ts.tv_sec * mult;
	return TIMEREL_CHECK(res);
}

struct timespec time_check_(struct timespec t, const char *abortstr)
{
	if (t.tv_sec < 0 || t.tv_nsec >= 1000000000) {
		if (abortstr) {
			fprintf(stderr, "%s: malformed time %li.%09li\n",
				abortstr,
				(long)t.tv_sec, (long)t.tv_nsec);
			abort();
		} else {
			struct timespec old = t;

			if (t.tv_nsec >= 1000000000) {
				t.tv_sec += t.tv_nsec / 1000000000;
				t.tv_nsec %= 1000000000;
			}
			if (t.tv_sec < 0)
				t.tv_sec = 0;

			fprintf(stderr, "WARNING: malformed time"
				" %li seconds %li ns converted to %li.%09li.\n",
				(long)old.tv_sec, (long)old.tv_nsec,
				(long)t.tv_sec, (long)t.tv_nsec);
		}
	}
	return t;
}

struct timerel timerel_check(struct timerel t, const char *abortstr)
{
	struct timerel ret;

	ret.ts = time_check_(t.ts, abortstr);
	return ret;
}

struct timeabs timeabs_check(struct timeabs t, const char *abortstr)
{
	struct timeabs ret;

	ret.ts = time_check_(t.ts, abortstr);
	return ret;
}