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
//! Interface to measure time.

use std::time::Instant;

/// The BSEC algorithem needs a clock capable of providing timestamps.
/// Implement this trait according to your hardware platform.
///
/// # Example
///
/// A possible implementation based on [`std::time`] could look like this:
///
/// ```
/// use bsec::clock::Clock;
/// use std::time::{Duration, Instant};
///
/// pub struct TimePassed {
///     start: Instant,
/// }
///
/// impl TimePassed {
///     fn new() -> Self {
///         Self { start: Instant::now() }
///     }
/// }
///
/// impl Clock for TimePassed {
///     fn timestamp_ns(&self) -> i64 {
///         self.start.elapsed().as_nanos() as i64
///     }
/// }
/// ```
pub trait Clock {
    /// Return a monotonically increasing timestamp with nanosecond resolution.
    ///
    /// The reference point may be arbitrary.
    fn timestamp_ns(&self) -> i64;
}

/// Measures time since the creation of an instance.
pub struct TimePassed {
    start: Instant,
}

impl TimePassed {
    /// Create a new instance starting time measurement at instant of
    /// instantiation.
    pub fn new() -> Self {
        Self {
            start: Instant::now(),
        }
    }
}

impl Default for TimePassed {
    fn default() -> Self {
        Self {
            start: Instant::now(),
        }
    }
}

impl Clock for TimePassed {
    fn timestamp_ns(&self) -> i64 {
        self.start.elapsed().as_nanos() as i64
    }
}

#[cfg(test)]
pub mod tests {
    use super::{Clock, TimePassed};

    #[test]
    fn time_passed_smoke_test() {
        assert!(TimePassed::default().timestamp_ns() >= 0);
    }
}

/// Provides a fake [`Clock`] implementation to be used in unit tests.
///
/// This module is only available if the **test-support** feature is enabled.
#[cfg(any(test, feature = "test-support"))]
pub mod test_support {
    use std::{
        sync::atomic::{AtomicI64, Ordering},
        time::Duration,
    };

    use super::*;

    /// A fake [`Clock`] for unit tests that can be advanced manually.
    ///
    /// Each call to [`FakeClock::timestamp_ns`] will advance by one nanosecond.
    ///
    /// # Example
    ///
    /// ```
    /// # use bsec::clock::{Clock, test_support::FakeClock};
    /// let clock = FakeClock::new();
    /// assert_eq!(clock.timestamp_ns(), 0);
    /// assert_eq!(clock.timestamp_ns(), 1);
    /// assert_eq!(clock.timestamp_ns(), 2);
    /// clock.advance_by(std::time::Duration::from_nanos(5));
    /// assert_eq!(clock.timestamp_ns(), 8);
    /// ```
    #[derive(Default)]
    pub struct FakeClock {
        timestamp_ns: AtomicI64,
    }

    impl FakeClock {
        /// Create a new [`FakeClock`] initialized with a zero timestamp.
        pub fn new() -> Self {
            Self::default()
        }
    }

    impl Clock for FakeClock {
        /// Returns the current timestamp and advances it by one.
        fn timestamp_ns(&self) -> i64 {
            self.timestamp_ns.fetch_add(1, Ordering::Relaxed)
        }
    }

    impl FakeClock {
        /// Advance the clock's internal time by `duration`.
        pub fn advance_by(&self, duration: Duration) {
            self.timestamp_ns
                .fetch_add(duration.as_nanos() as i64, Ordering::Relaxed);
        }
    }
}