response_time_analysis/
time.rs

1/*! A simple discrete time model with type-safe wrappers
2
3The analyses and definitions provided in this module are based on a
4simple discrete time model with a least, indivisible unit of time (e.g.,
5a processor cycle).
6
7To avoid accidental confusion of offsets, durations, and intervals, some
8type-safe wrappers are provided.
9
10 */
11
12use derive_more::{Add, AddAssign, Display, From, Into, Sub, Sum};
13
14/// This library uses a simple discrete time model.
15///
16/// We do not use unchecked arithmetic, so any over- or underflow
17/// will be detected by the Rust runtime system.
18pub type Time = u64;
19
20/// Type-safe alias for time value representing an offset.
21#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, From, Into, Display, Debug)]
22pub struct Offset {
23    val: Time,
24}
25
26impl Offset {
27    /// Given a duration `delta`, returns an offset `X` such that the
28    /// half-open interval `[0, X)` has length `delta`.
29    pub const fn from_time_zero(delta: Duration) -> Offset {
30        Offset { val: delta.val }
31    }
32
33    /// Given a duration `delta`, returns an offset `X` such that the
34    /// closed interval `[0, X]` has length `delta`.
35    pub const fn closed_from_time_zero(delta: Duration) -> Offset {
36        Offset { val: delta.val - 1 }
37    }
38
39    /// Given an offset `X`, returns the length of the half-open
40    /// interval `[0, X)`.
41    ///
42    /// # Example
43    /// ```
44    /// # use response_time_analysis::time::Offset;
45    /// let t = Offset::from(10);
46    /// assert_eq!(Offset::from_time_zero(t.since_time_zero()), t);
47    /// ```
48    pub const fn since_time_zero(self) -> Duration {
49        Duration { val: self.val }
50    }
51
52    /// Given an offset `X`, returns the length of the closed
53    /// interval `[0, X]`.
54    ///
55    /// # Example
56    /// ```
57    /// # use response_time_analysis::time::Offset;
58    /// let t = Offset::from(10);
59    /// assert_eq!(Offset::closed_from_time_zero(t.closed_since_time_zero()), t);
60    /// ```
61    pub const fn closed_since_time_zero(self) -> Duration {
62        Duration { val: self.val + 1 }
63    }
64
65    /// Compute the distance to a later point in time.
66    ///
67    /// # Example:
68    ///
69    /// ```
70    /// # use response_time_analysis::time::Offset;
71    /// let a = Offset::from(10);
72    /// let b = Offset::from(25);
73    /// assert_eq!(a + a.distance_to(b), b);
74    /// ```
75    pub fn distance_to(self, t: Offset) -> Duration {
76        debug_assert!(self.val <= t.val);
77        Duration::from(t.val - self.val)
78    }
79}
80
81impl std::ops::Add<Duration> for Offset {
82    type Output = Offset;
83
84    fn add(self, delta: Duration) -> Offset {
85        Offset {
86            val: self.val + delta.val,
87        }
88    }
89}
90
91/// Type-safe alias for time values representing an interval length.
92#[derive(
93    Clone,
94    Copy,
95    PartialEq,
96    Eq,
97    PartialOrd,
98    Ord,
99    From,
100    Into,
101    Display,
102    Debug,
103    Add,
104    AddAssign,
105    Sub,
106    Sum,
107)]
108pub struct Duration {
109    val: Time,
110}
111
112impl Duration {
113    /// Check whether the duration represents a non-empty interval.
114    pub const fn is_non_zero(self) -> bool {
115        self.val > 0
116    }
117
118    /// Check whether the duration represents an empty interval.
119    pub const fn is_zero(self) -> bool {
120        self.val == 0
121    }
122
123    /// Construct a duration value that represents the empty interval.
124    pub const fn zero() -> Duration {
125        Duration { val: 0 }
126    }
127
128    /// Construct a duration value that represents the smallest unit
129    /// of time (i.e., 1).
130    pub const fn epsilon() -> Duration {
131        Duration { val: 1 }
132    }
133
134    /// Subtract without under-flowing.
135    #[must_use]
136    pub const fn saturating_sub(&self, rhs: Duration) -> Duration {
137        Duration {
138            val: self.val.saturating_sub(rhs.val),
139        }
140    }
141}
142
143impl From<Service> for Duration {
144    fn from(s: Service) -> Duration {
145        Duration::from(s.val)
146    }
147}
148
149impl std::ops::Mul<u64> for Duration {
150    type Output = Duration;
151
152    fn mul(self, factor: u64) -> Duration {
153        Duration::from(self.val * factor)
154    }
155}
156
157impl std::ops::Div<Duration> for Duration {
158    type Output = u64;
159
160    fn div(self, divisor: Duration) -> u64 {
161        self.val / divisor.val
162    }
163}
164
165impl std::ops::Rem<Duration> for Duration {
166    type Output = Duration;
167
168    fn rem(self, divisor: Duration) -> Duration {
169        Duration::from(self.val % divisor.val)
170    }
171}
172
173/// Type-safe alias for time values representing some amount of
174/// processor service.
175#[derive(
176    Clone,
177    Copy,
178    PartialEq,
179    Eq,
180    PartialOrd,
181    Ord,
182    From,
183    Into,
184    Display,
185    Debug,
186    Add,
187    Sub,
188    AddAssign,
189    Sum,
190)]
191pub struct Service {
192    val: Time,
193}
194
195impl Service {
196    /// Construct a service value that represents zero service.
197    pub fn none() -> Service {
198        Service::from(0)
199    }
200
201    /// Check whether the value represents zero service.
202    pub fn is_none(self) -> bool {
203        self.val == 0
204    }
205
206    /// The amount of service received in interval of duration `d`
207    /// (assuming a unit-service processor).
208    pub const fn in_interval(d: Duration) -> Service {
209        Service { val: d.val }
210    }
211
212    /// The least unit of service.
213    pub const fn epsilon() -> Service {
214        Service { val: 1 }
215    }
216
217    /// Subtract without under-flowing.
218    #[must_use]
219    pub const fn saturating_sub(&self, rhs: Service) -> Service {
220        Service {
221            val: self.val.saturating_sub(rhs.val),
222        }
223    }
224}
225
226impl From<Duration> for Service {
227    fn from(d: Duration) -> Service {
228        Service::in_interval(d)
229    }
230}
231
232impl std::ops::Mul<u64> for Service {
233    type Output = Service;
234
235    fn mul(self, factor: u64) -> Service {
236        Service::from(self.val * factor)
237    }
238}