summaryrefslogtreecommitdiff
path: root/src/helpers/time/Time.cpp
blob: f454b7848edc49ff5edf5ed9011ceca1993df56d (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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#include "Time.hpp"

#define chr                   std::chrono
#define TIMESPEC_NSEC_PER_SEC 1000000000L

using s_ns = std::pair<uint64_t, uint64_t>;

static s_ns timediff(const s_ns& a, const s_ns& b) {
    s_ns d;

    d.first = a.first - b.first;
    if (a.second >= b.second)
        d.second = a.second - b.second;
    else {
        d.second = (TIMESPEC_NSEC_PER_SEC + a.second) - b.second;
        d.first -= 1;
    }

    return d;
}

static s_ns timeadd(const s_ns& a, const s_ns& b) {
    s_ns d;

    d.first = a.first + b.first;
    if (a.second + b.second >= TIMESPEC_NSEC_PER_SEC) {
        d.second = a.second + b.second - TIMESPEC_NSEC_PER_SEC;
        d.first += 1;
    } else
        d.second = a.second + b.second;

    return d;
}

Time::steady_tp Time::steadyNow() {
    return chr::steady_clock::now();
}

Time::system_tp Time::systemNow() {
    return chr::system_clock::now();
}

uint64_t Time::millis(const steady_tp& tp) {
    return chr::duration_cast<chr::milliseconds>(tp.time_since_epoch()).count();
}

s_ns Time::secNsec(const steady_tp& tp) {
    const uint64_t sec     = chr::duration_cast<chr::seconds>(tp.time_since_epoch()).count();
    const auto     nsecdur = tp - chr::steady_clock::time_point(chr::seconds(sec));
    return {sec, chr::duration_cast<chr::nanoseconds>(nsecdur).count()};
}

uint64_t Time::millis(const system_tp& tp) {
    return chr::duration_cast<chr::milliseconds>(tp.time_since_epoch()).count();
}

s_ns Time::secNsec(const system_tp& tp) {
    const uint64_t sec     = chr::duration_cast<chr::seconds>(tp.time_since_epoch()).count();
    const auto     nsecdur = tp - chr::system_clock::time_point(chr::seconds(sec));
    return {sec, chr::duration_cast<chr::nanoseconds>(nsecdur).count()};
}

// TODO: this is a mess, but C++ doesn't define what steady_clock is.
// At least on Linux, system_clock == CLOCK_REALTIME
// and steady_clock == CLOCK_MONOTONIC,
// or at least it seems so with gcc and gcc's stl.
// but, since we can't *ever* be sure, we have to guess.
// In general, this may shift the time around by a couple hundred ns. Doesn't matter, realistically.

Time::steady_tp Time::fromTimespec(const timespec* ts) {
    timespec mono{}, real{};
    clock_gettime(CLOCK_MONOTONIC, &mono);
    clock_gettime(CLOCK_REALTIME, &real);
    auto now    = Time::steadyNow();
    auto nowSys = Time::systemNow();
    s_ns stdSteady, stdReal;
    stdSteady = Time::secNsec(now);
    stdReal   = Time::secNsec(nowSys);

    // timespec difference, REAL - MONO
    s_ns diff;
    diff.first = real.tv_sec - mono.tv_sec;
    if (real.tv_nsec >= mono.tv_nsec)
        diff.second = real.tv_nsec - mono.tv_nsec;
    else {
        diff.second = TIMESPEC_NSEC_PER_SEC + real.tv_nsec - mono.tv_nsec;
        diff.first -= 1;
    }

    // STD difference, REAL - MONO
    s_ns diff2 = timediff(stdReal, stdSteady);

    s_ns diffFinal;
    s_ns monotime = {ts->tv_sec, ts->tv_nsec};

    if (diff.first >= diff2.first || (diff.first == diff2.first && diff.second >= diff2.second))
        diffFinal = timediff(diff, diff2);
    else
        diffFinal = timediff(diff2, diff);

    auto sum = timeadd(monotime, diffFinal);
    return chr::steady_clock::time_point(std::chrono::seconds(sum.first)) + chr::nanoseconds(sum.second);
}

struct timespec Time::toTimespec(const steady_tp& tp) {
    timespec mono{}, real{};
    clock_gettime(CLOCK_MONOTONIC, &mono);
    clock_gettime(CLOCK_REALTIME, &real);
    Time::steady_tp now    = Time::steadyNow();
    Time::system_tp nowSys = Time::systemNow();
    s_ns            stdSteady, stdReal;
    stdSteady = Time::secNsec(now);
    stdReal   = Time::secNsec(nowSys);

    // timespec difference, REAL - MONO
    s_ns diff;
    diff.first = real.tv_sec - mono.tv_sec;
    if (real.tv_nsec >= mono.tv_nsec)
        diff.second = real.tv_nsec - mono.tv_nsec;
    else {
        diff.second = TIMESPEC_NSEC_PER_SEC + real.tv_nsec - mono.tv_nsec;
        diff.first -= 1;
    }

    // STD difference, REAL - MONO
    s_ns diff2 = timediff(stdReal, stdSteady);

    s_ns diffFinal;
    s_ns tpTime = secNsec(tp);

    if (diff.first >= diff2.first || (diff.first == diff2.first && diff.second >= diff2.second))
        diffFinal = timediff(diff, diff2);
    else
        diffFinal = timediff(diff2, diff);

    auto sum = timeadd(tpTime, diffFinal);
    return timespec{.tv_sec = sum.first, .tv_nsec = sum.second};
}

Time::steady_dur Time::till(const timespec& ts) {
    timespec mono{};
    clock_gettime(CLOCK_MONOTONIC, &mono);
    const auto delay = (ts.tv_sec - mono.tv_sec) * 1000000000 + (ts.tv_nsec - mono.tv_nsec);
    return std::chrono::nanoseconds(delay);
}