Skip to main content

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        assert!(!overflowed, "overflow while subtracting two TickInstants.");
168        TickDuration { ticks: difference }
169    }
170}
171
172impl Add for TickDuration {
173    type Output = TickDuration;
174
175    fn add(self, rhs: Self) -> Self::Output {
176        let mut result = self;
177        result += rhs;
178        result
179    }
180}
181
182impl AddAssign for TickDuration {
183    fn add_assign(&mut self, rhs: Self) {
184        self.ticks = self
185            .ticks
186            .checked_add(rhs.ticks)
187            .expect("Overflow occurred while adding TickDuration instances.")
188    }
189}
190
191impl Sub for TickDuration {
192    type Output = TickDuration;
193
194    fn sub(self, rhs: Self) -> Self::Output {
195        let mut result = self;
196        result -= rhs;
197        result
198    }
199}
200
201impl SubAssign for TickDuration {
202    fn sub_assign(&mut self, rhs: Self) {
203        self.ticks = self
204            .ticks
205            .checked_sub(rhs.ticks)
206            .expect("Overflow occurred while subtracting TickDuration instances.");
207    }
208}
209
210impl Neg for TickDuration {
211    type Output = TickDuration;
212
213    fn neg(self) -> Self::Output {
214        TickDuration {
215            ticks: self
216                .ticks
217                .checked_neg()
218                .expect("Overflow while negating duration."),
219        }
220    }
221}
222
223impl Display for TickInstant {
224    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
225        write!(f, "[{}]", self.0)
226    }
227}
228
229impl Display for TickDuration {
230    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
231        write!(f, "<{}>", self.ticks)
232    }
233}
234
235impl From<TickInstant> for u64 {
236    fn from(value: TickInstant) -> Self {
237        value.0
238    }
239}
240
241impl From<TickDuration> for i64 {
242    fn from(value: TickDuration) -> Self {
243        value.ticks
244    }
245}