1use core::time::Duration;
7
8pub const STEPS_PER_SECOND: u32 = shuyi_core::timer::STEPS_PER_SECOND;
10
11pub const MAX_STEPS: u32 = 4;
14
15const SECOND: u64 = 1_000_000_000;
17
18#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
21pub struct TickClock {
22 owed: u64,
25}
26
27impl TickClock {
28 pub const fn new() -> Self {
30 Self { owed: 0 }
31 }
32
33 pub fn advance(&mut self, elapsed: Duration) -> u32 {
37 let nanos = u64::try_from(elapsed.as_nanos()).unwrap_or(u64::MAX);
38 self.owed = self
39 .owed
40 .saturating_add(nanos.saturating_mul(u64::from(STEPS_PER_SECOND)));
41 let due = self.owed / SECOND;
42 self.owed %= SECOND;
43 u32::try_from(due.min(u64::from(MAX_STEPS))).unwrap_or(MAX_STEPS)
45 }
46
47 pub fn reset(&mut self) {
49 self.owed = 0;
50 }
51}
52
53#[cfg(test)]
54mod tests {
55 use super::*;
56 use crate::alloc_prelude::*;
57
58 const STEP_NS: u64 = 16_666_667;
59
60 #[test]
61 fn a_frame_of_a_sixtieth_of_a_second_is_one_step() {
62 let mut clock = TickClock::new();
63 let frame = Duration::from_nanos(STEP_NS);
64 let steps: u32 = (0..600).map(|_| clock.advance(frame)).sum();
65 assert_eq!(steps, 600);
66 }
67
68 #[test]
69 fn whole_milliseconds_add_up_to_sixty_steps_a_second() {
70 let mut clock = TickClock::new();
71 let frames = [16, 17, 17].map(Duration::from_millis);
74 let steps: Vec<u32> = frames
75 .iter()
76 .cycle()
77 .take(60)
78 .map(|&frame| clock.advance(frame))
79 .collect();
80 assert_eq!(steps.iter().sum::<u32>(), 60);
81 assert!(steps.iter().all(|&s| s <= 2), "{steps:?}");
82 let mut fast = TickClock::new();
84 let steps: Vec<u32> = (0..120)
85 .map(|_| fast.advance(Duration::from_nanos(STEP_NS / 2 + 1)))
86 .collect();
87 assert_eq!(steps.iter().sum::<u32>(), 60);
88 assert!(steps.chunks(2).all(|pair| pair.iter().sum::<u32>() == 1));
89 }
90
91 #[test]
92 fn a_late_frame_runs_at_most_four_steps_and_drops_the_rest() {
93 let mut clock = TickClock::new();
94 assert_eq!(clock.advance(Duration::from_secs(600)), MAX_STEPS);
95 assert_eq!(clock.advance(Duration::from_millis(1)), 0);
97 assert_eq!(clock.advance(Duration::MAX), MAX_STEPS);
98 assert_eq!(clock.advance(Duration::ZERO), 0);
99 assert_eq!(clock.advance(Duration::from_nanos(3 * STEP_NS)), 3);
101 }
102
103 #[test]
104 fn reset_forgets_a_part_step() {
105 let mut clock = TickClock::new();
106 assert_eq!(clock.advance(Duration::from_millis(10)), 0);
107 clock.reset();
108 assert_eq!(clock.advance(Duration::from_millis(10)), 0);
109 assert_eq!(clock.advance(Duration::from_millis(10)), 1);
110 }
111}