fine_grained/
stopwatch.rs

1// Copyright 2017 Bastian Meyer
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or http://apache.org/licenses/LICENSE-2.0> or the
4// MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option. This file may not be copied,
5// modified, or distributed except according to those terms.
6
7//! The actual stopwatch implementation.
8
9use std::fmt;
10
11use time;
12
13/// A stopwatch with lap functionality and nanosecond resolution.
14#[derive(Clone, Debug, Default)]
15pub struct Stopwatch {
16    /// A list of all lap measurements.
17    laps: Vec<u64>,
18
19    /// The start time of the currently running lap, or `None` if the stopwatch is not running.
20    start_time: Option<u64>,
21
22    /// The sum of all finished laps.
23    total_time: u64
24}
25
26impl Stopwatch {
27    /// Initialize a new stopwatch without starting it.
28    pub fn new() -> Stopwatch {
29        Stopwatch { laps: vec![], start_time: None, total_time: 0 }
30    }
31
32    /// Initialize a new stopwatch and start it.
33    pub fn start_new() -> Stopwatch {
34        let mut stopwatch = Stopwatch::new();
35        stopwatch.start();
36        stopwatch
37    }
38
39    /// Start the stopwatch.
40    pub fn start(&mut self) {
41        self.start_time = Some(time::precise_time_ns());
42    }
43
44    /// Start a new lap. Save the last lap's time and return it.
45    ///
46    /// If the stopwatch has not been started, the lap will not be saved and `0` will be returned.
47    pub fn lap(&mut self) -> u64 {
48        // Determine this lap's duration. If the stopwatch has not been started, the lap is meaningless.
49        let current_time: u64 = time::precise_time_ns();
50        let lap: u64 = match self.start_time {
51            Some(t) => current_time - t,
52            None => return 0
53        };
54
55        // Add this lap's duration to the total time, add this lap to the list of laps,
56        // and reset the starting time for the new lap.
57        self.total_time += lap;
58        self.laps.push(lap);
59        self.start_time = Some(current_time);
60
61        lap
62    }
63
64    /// Stop the stopwatch, without updating the total time.
65    ///
66    /// This will not reset the stopwatch, i.e. the total time and the laps will be preserved.
67    pub fn stop(&mut self) {
68        self.start_time = None;
69    }
70
71    /// Re-initialize the stopwatch without restarting it.
72    pub fn reset(&mut self) {
73        self.laps = vec![];
74        self.start_time = None;
75        self.total_time = 0;
76    }
77
78    /// Re-initialize the stopwatch and start it.
79    pub fn restart(&mut self) {
80        self.reset();
81        self.start();
82    }
83
84    /// Get the total time the stopwatch has been running.
85    ///
86    /// If the stopwatch is still running, the total time is the time from starting the
87    /// stopwatch until now. Otherwise, it is the sum of all laps.
88    pub fn total_time(&self) -> u64 {
89        match self.start_time {
90            Some(current_lap_start_time) => {
91                /// If the stopwatch is currently running, the total time is the saved total time
92                /// plus the current lap's duration up to this point.
93                let current_time: u64 = time::precise_time_ns();
94                let lap: u64 = current_time - current_lap_start_time;
95                self.total_time + lap
96            },
97            None => self.total_time
98        }
99    }
100
101    /// Get the list of all measured lap times in the order the laps were timed.
102    pub fn laps(&self) -> &Vec<u64> {
103        &self.laps
104    }
105
106    /// Get the number of measured laps.
107    pub fn number_of_laps(&self) -> usize {
108        self.laps.len()
109    }
110
111    /// Determine if the stopwatch is currently running.
112    pub fn is_running(&self) -> bool {
113        self.start_time.is_some()
114    }
115}
116
117impl fmt::Display for Stopwatch {
118    /// Formats the total time using the given formatter.
119    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
120        write!(formatter, "{total_time}ns", total_time = self.total_time())
121    }
122}
123
124#[cfg(test)]
125mod tests {
126    #![allow(unused_results)]
127
128    use super::Stopwatch;
129
130    #[test]
131    fn new() {
132        let stopwatch = Stopwatch::new();
133        assert_eq!(stopwatch.laps, vec![]);
134        assert_eq!(stopwatch.start_time, None);
135        assert_eq!(stopwatch.total_time, 0);
136    }
137
138    #[test]
139    fn start_new() {
140        let stopwatch = Stopwatch::start_new();
141        assert_eq!(stopwatch.laps, vec![]);
142        assert!(stopwatch.start_time.is_some());
143        assert_eq!(stopwatch.total_time, 0);
144    }
145
146    #[test]
147    fn start() {
148        let mut stopwatch = Stopwatch::new();
149        stopwatch.start();
150        assert!(stopwatch.start_time.is_some());
151    }
152
153    #[test]
154    fn lap() {
155        let mut stopwatch = Stopwatch::new();
156        let lap_0: u64 = stopwatch.lap();
157        assert_eq!(lap_0, 0);
158        assert_eq!(stopwatch.laps.len(), 0);
159        assert_eq!(stopwatch.total_time, lap_0);
160
161        stopwatch.start();
162        let lap_1: u64 = stopwatch.lap();
163        assert!(lap_1 > 0);
164        assert_eq!(stopwatch.laps.len(), 1);
165        assert_eq!(stopwatch.laps[0], lap_1);
166        assert_eq!(stopwatch.total_time, lap_1);
167
168        let lap_2: u64 = stopwatch.lap();
169        assert!(lap_2 > 0);
170        assert_eq!(stopwatch.laps.len(), 2);
171        assert_eq!(stopwatch.laps[0], lap_1);
172        assert_eq!(stopwatch.laps[1], lap_2);
173        assert_eq!(stopwatch.total_time, lap_1 + lap_2);
174    }
175
176    #[test]
177    fn stop() {
178        let mut stopwatch = Stopwatch::start_new();
179        let lap: u64 = stopwatch.lap();
180        stopwatch.stop();
181        assert!(stopwatch.start_time.is_none());
182        assert_eq!(stopwatch.laps.len(), 1);
183        assert_eq!(stopwatch.total_time, lap);
184    }
185
186    #[test]
187    fn reset() {
188        let mut stopwatch = Stopwatch::start_new();
189        stopwatch.lap();
190        stopwatch.reset();
191        assert_eq!(stopwatch.laps, vec![]);
192        assert_eq!(stopwatch.start_time, None);
193        assert_eq!(stopwatch.total_time, 0);
194    }
195
196    #[test]
197    fn restart() {
198        let mut stopwatch = Stopwatch::start_new();
199        stopwatch.lap();
200        stopwatch.restart();
201        assert_eq!(stopwatch.laps, vec![]);
202        assert!(stopwatch.start_time.is_some());
203        assert_eq!(stopwatch.total_time, 0);
204    }
205
206    #[test]
207    fn total_time() {
208        let mut stopwatch = Stopwatch::start_new();
209        let start_time: u64 = stopwatch.start_time.unwrap();
210        let mut total_time: u64 = stopwatch.total_time();
211        assert!(total_time > 0);
212        assert_eq!(stopwatch.total_time, 0);
213        assert_eq!(stopwatch.laps, vec![]);
214        assert_eq!(stopwatch.start_time.unwrap(), start_time);
215
216        stopwatch.lap();
217        stopwatch.stop();
218        total_time = stopwatch.total_time();
219        assert_eq!(total_time, stopwatch.total_time);
220    }
221
222    #[test]
223    fn laps() {
224        let mut stopwatch = Stopwatch::start_new();
225        stopwatch.lap();
226        stopwatch.lap();
227        stopwatch.lap();
228
229        assert_eq!(stopwatch.laps(), &stopwatch.laps);
230    }
231
232    #[test]
233    fn number_of_laps() {
234        let mut stopwatch = Stopwatch::start_new();
235        stopwatch.lap();
236        stopwatch.lap();
237        stopwatch.lap();
238
239        assert_eq!(stopwatch.number_of_laps(), 3);
240    }
241
242    #[test]
243    fn is_running() {
244        let mut stopwatch = Stopwatch::new();
245        assert!(!stopwatch.is_running());
246
247        stopwatch.start();
248        assert!(stopwatch.is_running());
249    }
250
251    #[test]
252    fn fmt_display() {
253        let mut stopwatch = Stopwatch::new();
254        stopwatch.total_time = 42;
255        assert_eq!(format!("{stopwatch}", stopwatch = stopwatch), "42ns");
256    }
257}