dfir_rs/scheduled/
ticks.rs

1//! This module contains types to work with ticks.
2//!
3//! Each iteration of a process loop is called a tick. Associated with the process
4//! is a clock value, which tells you how many ticks were executed by this process prior to the
5//! current tick. Each process produces totally ordered, sequentially increasing clock values,
6//! which you can think of as the "local logical time" at the process.
7
8use std::fmt::{Display, Formatter};
9use std::ops::{Add, AddAssign, Neg, Sub, SubAssign};
10
11use serde::{Deserialize, Serialize};
12
13/// A point in time during execution on process.
14///
15/// `TickInstant` instances can be subtracted to calculate the `TickDuration` between them.
16///
17/// ```
18/// # use dfir_rs::scheduled::ticks::{TickDuration, TickInstant};
19///
20/// assert_eq!(TickInstant(1) - TickInstant(0), TickDuration::SINGLE_TICK);
21/// assert_eq!(TickInstant(0) - TickInstant(1), -TickDuration::SINGLE_TICK);
22/// ```
23#[derive(
24    Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Default, Debug, Serialize, Deserialize,
25)]
26pub struct TickInstant(pub u64);
27
28/// The duration between two ticks.
29///
30/// `TickDuration` instances can be negative to allow for calculation of `TickInstant` instances in the past.
31///
32/// ```
33/// # use dfir_rs::scheduled::ticks::{TickDuration, TickInstant};
34/// assert_eq!(TickInstant(1) + TickDuration::new(-1), TickInstant(0))
35/// ```
36/// `TickDuration` instances can be added/subtracted to/from other `TickDuration` instances
37///
38/// ```
39/// # use dfir_rs::scheduled::ticks::TickDuration;
40/// assert_eq!(TickDuration::ZERO + TickDuration::ZERO, TickDuration::ZERO);
41/// assert_eq!(
42///     TickDuration::ZERO + TickDuration::SINGLE_TICK,
43///     TickDuration::SINGLE_TICK
44/// );
45/// assert_eq!(
46///     TickDuration::SINGLE_TICK - TickDuration::ZERO,
47///     TickDuration::SINGLE_TICK
48/// );
49/// assert_eq!(
50///     TickDuration::SINGLE_TICK - TickDuration::SINGLE_TICK,
51///     TickDuration::ZERO
52/// );
53/// assert_eq!(
54///     TickDuration::ZERO - TickDuration::SINGLE_TICK,
55///     -TickDuration::SINGLE_TICK
56/// );
57/// ```
58#[derive(
59    Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Default, Debug, Serialize, Deserialize,
60)]
61pub struct TickDuration {
62    /// The length of the duration, measured in ticks.
63    pub ticks: i64,
64}
65
66impl TickInstant {
67    /// Create a new TickInstant
68    ///
69    /// The specified parameter indicates the number of ticks that have elapsed on the process,
70    /// prior to this one.
71    pub fn new(ticks: u64) -> Self {
72        TickInstant(ticks)
73    }
74}
75
76impl TickDuration {
77    /// A zero duration
78    ///
79    /// It is the identity element for addition for both `TickDuration` and
80    /// `TickInstant` (i.e. adding zero duration to a `TickInstant` or `TickDuration` results in
81    /// the same `TickInstant` or `TickDuration`.
82    ///
83    /// ```
84    /// # use dfir_rs::scheduled::ticks::{TickDuration, TickInstant};
85    /// # use dfir_lang::graph::ops::DelayType::Tick;
86    /// let ticks = TickInstant::new(100);
87    /// assert_eq!(ticks + TickDuration::ZERO, ticks);
88    /// assert_eq!(ticks - TickDuration::ZERO, ticks);
89    ///
90    /// let duration = TickDuration::new(100);
91    /// assert_eq!(duration + TickDuration::ZERO, duration);
92    /// assert_eq!(duration - TickDuration::ZERO, duration);
93    /// ```
94    pub const ZERO: Self = TickDuration { ticks: 0 };
95
96    /// A single tick duration.
97    ///
98    /// It is the duration between two consecutive `TickInstant` instances.
99    ///
100    /// ```
101    /// # use dfir_rs::scheduled::ticks::{TickDuration, TickInstant};
102    /// assert_eq!(TickInstant(0) + TickDuration::SINGLE_TICK, TickInstant(1))
103    /// ```
104    pub const SINGLE_TICK: Self = TickDuration { ticks: 1 };
105
106    /// Create a new `TickDuration` for the specified tick interval.
107    ///
108    /// A negative duration allows for calculating `TickInstants` in the past and represents a
109    /// backward movement in time.
110    pub fn new(ticks: i64) -> TickDuration {
111        TickDuration { ticks }
112    }
113}
114
115impl Add<TickDuration> for TickInstant {
116    type Output = TickInstant;
117
118    fn add(self, rhs: TickDuration) -> Self::Output {
119        let mut result = self;
120        result += rhs;
121        result
122    }
123}
124
125impl AddAssign<TickDuration> for TickInstant {
126    fn add_assign(&mut self, rhs: TickDuration) {
127        self.0 = self
128            .0
129            .checked_add_signed(rhs.ticks)
130            .expect("overflow while adding tick duration to tick instant.");
131    }
132}
133
134impl Sub<TickDuration> for TickInstant {
135    type Output = TickInstant;
136
137    fn sub(self, rhs: TickDuration) -> Self::Output {
138        let mut result = self;
139        result -= rhs;
140        result
141    }
142}
143
144impl SubAssign<TickDuration> for TickInstant {
145    fn sub_assign(&mut self, rhs: TickDuration) {
146        if rhs.ticks.is_positive() {
147            self.0 = self
148                .0
149                .checked_sub(rhs.ticks.unsigned_abs())
150                .expect("overflow while subtracting duration from instant.");
151        } else if rhs.ticks.is_negative() {
152            self.0 = self
153                .0
154                .checked_add(rhs.ticks.unsigned_abs())
155                .expect("overflow while subtracting duration from instant.")
156        }
157    }
158}
159
160impl Sub for TickInstant {
161    type Output = TickDuration;
162
163    fn sub(self, rhs: TickInstant) -> Self::Output {
164        let minuend = (self.0 as i64).wrapping_add(i64::MIN);
165        let subtrahend = (rhs.0 as i64).wrapping_add(i64::MIN);
166        let (difference, overflowed) = minuend.overflowing_sub(subtrahend);
167        if overflowed {
168            panic!("overflow while subtracting two TickInstants.")
169        }
170        TickDuration { ticks: difference }
171    }
172}
173
174impl Add for TickDuration {
175    type Output = TickDuration;
176
177    fn add(self, rhs: Self) -> Self::Output {
178        let mut result = self;
179        result += rhs;
180        result
181    }
182}
183
184impl AddAssign for TickDuration {
185    fn add_assign(&mut self, rhs: Self) {
186        self.ticks = self
187            .ticks
188            .checked_add(rhs.ticks)
189            .expect("Overflow occurred while adding TickDuration instances.")
190    }
191}
192
193impl Sub for TickDuration {
194    type Output = TickDuration;
195
196    fn sub(self, rhs: Self) -> Self::Output {
197        let mut result = self;
198        result -= rhs;
199        result
200    }
201}
202
203impl SubAssign for TickDuration {
204    fn sub_assign(&mut self, rhs: Self) {
205        self.ticks = self
206            .ticks
207            .checked_sub(rhs.ticks)
208            .expect("Overflow occurred while subtracting TickDuration instances.");
209    }
210}
211
212impl Neg for TickDuration {
213    type Output = TickDuration;
214
215    fn neg(self) -> Self::Output {
216        TickDuration {
217            ticks: self
218                .ticks
219                .checked_neg()
220                .expect("Overflow while negating duration."),
221        }
222    }
223}
224
225impl Display for TickInstant {
226    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
227        write!(f, "[{}]", self.0)
228    }
229}
230
231impl Display for TickDuration {
232    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
233        write!(f, "<{}>", self.ticks)
234    }
235}
236
237impl From<TickInstant> for u64 {
238    fn from(value: TickInstant) -> Self {
239        value.0
240    }
241}
242
243impl From<TickDuration> for i64 {
244    fn from(value: TickDuration) -> Self {
245        value.ticks
246    }
247}