turbo_tasks/
small_duration.rs

1use std::{
2    fmt::{Debug, Display},
3    time::Duration,
4};
5
6/// Stores a [`Duration`] in a given precision (in nanoseconds) in 4 bytes.
7///
8/// For instance, for `P = 10_000` (10 microseconds), this allows a for a total
9/// duration of 11.9 hours. Values smaller than 10 microseconds are stored as 10
10/// microseconds.
11#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Default)]
12pub struct SmallDuration<const P: u64>(u32);
13
14impl<const P: u64> SmallDuration<P> {
15    pub const ZERO: SmallDuration<P> = SmallDuration(0);
16    // TODO(alexkirsz) Figure out if MIN should be 0 or 1.
17    pub const MIN: SmallDuration<P> = SmallDuration(1);
18    pub const MAX: SmallDuration<P> = SmallDuration(u32::MAX);
19
20    pub const fn from_nanos(nanos: u64) -> Self {
21        if nanos == 0 {
22            return SmallDuration::ZERO;
23        }
24        if nanos <= P {
25            return SmallDuration::MIN;
26        }
27        let value = nanos / P;
28        if value > u32::MAX as u64 {
29            return SmallDuration::MAX;
30        }
31        SmallDuration(value as u32)
32    }
33
34    pub const fn from_micros(micros: u64) -> Self {
35        if micros == 0 {
36            return SmallDuration::ZERO;
37        }
38        let micros_precision = P / 1_000;
39        if micros <= micros_precision {
40            return SmallDuration::MIN;
41        }
42        let value = micros * 1_000 / P;
43        if value > u32::MAX as u64 {
44            return SmallDuration::MAX;
45        }
46        SmallDuration(value as u32)
47    }
48
49    pub const fn from_millis(millis: u64) -> Self {
50        if millis == 0 {
51            return SmallDuration::ZERO;
52        }
53        let millis_precision = P / 1_000_000;
54        if millis <= millis_precision {
55            return SmallDuration::MIN;
56        }
57        let value = millis * 1_000_000 / P;
58        if value > u32::MAX as u64 {
59            return SmallDuration::MAX;
60        }
61        SmallDuration(value as u32)
62    }
63
64    pub const fn from_secs(secs: u64) -> Self {
65        if secs == 0 {
66            return SmallDuration::ZERO;
67        }
68        let secs_precision = P / 1_000_000_000;
69        if secs <= secs_precision {
70            return SmallDuration::MIN;
71        }
72        let value = secs * 1_000_000_000 / P;
73        if value > u32::MAX as u64 {
74            return SmallDuration::MAX;
75        }
76        SmallDuration(value as u32)
77    }
78
79    pub(self) fn to_duration(self) -> Duration {
80        Duration::from_nanos(self.0 as u64 * P)
81    }
82}
83
84impl<const P: u64> From<Duration> for SmallDuration<P> {
85    fn from(duration: Duration) -> Self {
86        if duration.is_zero() {
87            return SmallDuration::ZERO;
88        }
89        let nanos = duration.as_nanos();
90        if nanos <= P as u128 {
91            return SmallDuration::MIN;
92        }
93        (nanos / P as u128)
94            .try_into()
95            .map_or(SmallDuration::MAX, SmallDuration)
96    }
97}
98
99impl<const P: u64> From<SmallDuration<P>> for Duration {
100    fn from(duration: SmallDuration<P>) -> Self {
101        duration.to_duration()
102    }
103}
104
105impl<const P: u64> Display for SmallDuration<P> {
106    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
107        let duration = Duration::from(*self);
108        duration.fmt(f)
109    }
110}
111
112impl<const P: u64> Debug for SmallDuration<P> {
113    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
114        let duration = Duration::from(*self);
115        duration.fmt(f)
116    }
117}
118
119impl<const P: u64> PartialEq<Duration> for SmallDuration<P> {
120    fn eq(&self, other: &Duration) -> bool {
121        self.to_duration() == *other
122    }
123}
124
125#[cfg(test)]
126mod tests {
127    use std::time::Duration;
128
129    use super::SmallDuration;
130
131    #[test]
132    fn test_1_nano() {
133        type Sd = SmallDuration<1>;
134
135        assert_eq!(Sd::from_nanos(1), Duration::from_nanos(1));
136        assert_eq!(Sd::from_nanos(42), Duration::from_nanos(42));
137
138        assert_eq!(Sd::from_micros(1), Duration::from_micros(1));
139        assert_eq!(Sd::from_micros(42), Duration::from_micros(42));
140
141        assert_eq!(Sd::from_millis(1), Duration::from_millis(1));
142        assert_eq!(Sd::from_millis(42), Duration::from_millis(42));
143
144        assert_eq!(Sd::from_secs(1), Duration::from_secs(1));
145
146        // 1ns precision can only store up to ~4.29s.
147        assert_eq!(Sd::from_secs(4), Duration::from_secs(4));
148        assert_eq!(Sd::from_secs(5), Sd::MAX);
149    }
150
151    #[test]
152    fn test_1_micro() {
153        type Sd = SmallDuration<1_000>;
154
155        // 1µs precision can't store ns-level variations.
156        assert_eq!(Sd::from_nanos(1), Sd::MIN);
157        assert_eq!(Sd::from_nanos(42), Sd::MIN);
158
159        assert_eq!(Sd::from_micros(1), Duration::from_micros(1));
160        assert_eq!(Sd::from_micros(42), Duration::from_micros(42));
161
162        assert_eq!(Sd::from_millis(1), Duration::from_millis(1));
163        assert_eq!(Sd::from_millis(42), Duration::from_millis(42));
164
165        assert_eq!(Sd::from_secs(1), Duration::from_secs(1));
166        assert_eq!(Sd::from_secs(42), Duration::from_secs(42));
167
168        // 1µs precision can only store up to ~4,294s.
169        assert_eq!(Sd::from_secs(4_000), Duration::from_secs(4_000));
170        assert_eq!(Sd::from_secs(5_000), Sd::MAX);
171    }
172
173    #[test]
174    fn test_1_milli() {
175        type Sd = SmallDuration<1_000_000>;
176
177        // 1ms precision can't store ns-or-µs-level variations.
178        assert_eq!(Sd::from_nanos(1), Sd::MIN);
179        assert_eq!(Sd::from_nanos(42), Sd::MIN);
180        assert_eq!(Sd::from_micros(1), Sd::MIN);
181        assert_eq!(Sd::from_micros(42), Sd::MIN);
182
183        assert_eq!(Sd::from_millis(1), Duration::from_millis(1));
184        assert_eq!(Sd::from_millis(42), Duration::from_millis(42));
185
186        assert_eq!(Sd::from_secs(1), Duration::from_secs(1));
187        assert_eq!(Sd::from_secs(42), Duration::from_secs(42));
188
189        // 1ms precision can only store up to ~4,294,000s.
190        assert_eq!(Sd::from_secs(4_000_000), Duration::from_secs(4_000_000));
191        assert_eq!(Sd::from_secs(5_000_000), Sd::MAX);
192    }
193
194    #[test]
195    fn test_1_sec() {
196        type Sd = SmallDuration<1_000_000_000>;
197
198        // 1ms precision can't store ns/µs/ms-level variations.
199        assert_eq!(Sd::from_nanos(1), Sd::MIN);
200        assert_eq!(Sd::from_nanos(42), Sd::MIN);
201        assert_eq!(Sd::from_micros(1), Sd::MIN);
202        assert_eq!(Sd::from_micros(42), Sd::MIN);
203        assert_eq!(Sd::from_millis(1), Sd::MIN);
204        assert_eq!(Sd::from_millis(42), Sd::MIN);
205
206        assert_eq!(Sd::from_secs(1), Duration::from_secs(1));
207        assert_eq!(Sd::from_secs(42), Duration::from_secs(42));
208
209        // 1s precision can only store up to ~4,294,000,000s.
210        assert_eq!(
211            Sd::from_secs(4_000_000_000),
212            Duration::from_secs(4_000_000_000)
213        );
214        assert_eq!(Sd::from_secs(5_000_000_000), Sd::MAX);
215    }
216}