gametime/
freq.rs

1//! Contains types and functions to work with frequencies.
2
3use core::{iter::FusedIterator, num::NonZeroU64, ops};
4
5use crate::{
6    gcd,
7    span::TimeSpan,
8    stamp::TimeStamp,
9    step::ClockStep,
10};
11
12#[cfg(feature = "serde")]
13use serde::ser::SerializeTupleStruct;
14
15/// Represents frequency as a rational number.
16#[derive(Clone, Copy)]
17pub struct Frequency {
18    /// Number of periods in one cycle.
19    pub count: u64,
20
21    /// Number of nanoseconds in one cycle.
22    pub cycle: NonZeroU64,
23}
24
25impl Frequency {
26    /// Creates new frequency from number of periods in one cycle and cycle time span.
27    /// Uses non-zero time span for cycle.
28    ///
29    /// # Panics
30    ///
31    /// Panics if `period` is zero.
32    #[must_use]
33    pub fn new(count: u64, period: TimeSpan) -> Self {
34        assert_ne!(period, TimeSpan::ZERO, "Frequency period cannot be zero");
35
36        let period = period.as_nanos().unsigned_abs();
37        let gcd = gcd(count, period);
38        let count = count / gcd;
39        let period_nanos = period / gcd;
40
41        match NonZeroU64::new(period_nanos) {
42            None => unreachable!(),
43            Some(cycle) => Frequency { count, cycle },
44        }
45    }
46
47    /// Creates frequency from number of Hertz.
48    #[inline]
49    #[must_use]
50    pub fn from_hz(value: u64) -> Self {
51        Frequency::new(value, TimeSpan::SECOND)
52    }
53
54    /// Creates frequency from number of `KiloHertz`.
55    #[inline]
56    #[must_use]
57    pub fn from_khz(value: u64) -> Self {
58        Frequency::new(value, TimeSpan::MILLISECOND)
59    }
60
61    /// Creates frequency from number of `MegaHertz`.
62    #[inline]
63    #[must_use]
64    pub fn from_mhz(value: u64) -> Self {
65        Frequency::new(value, TimeSpan::MICROSECOND)
66    }
67
68    /// Creates frequency from number of `GigaHertz`.
69    #[inline]
70    #[must_use]
71    pub fn from_ghz(value: u64) -> Self {
72        Frequency::new(value, TimeSpan::NANOSECOND)
73    }
74
75    /// Element is a nanosecond divided by count of periods in one cycle.
76    /// Return number of elements in the given time span.
77    #[inline]
78    fn elements(&self, span: TimeSpan) -> Elements {
79        #![allow(clippy::cast_sign_loss)] // Sign loss is not possible due to check.
80
81        debug_assert!(!span.is_negative(), "Span must not be negative");
82
83        Elements(span.as_nanos() as u64 * self.count)
84    }
85
86    /// Returns the number of periods in the given time span.
87    #[inline]
88    #[must_use]
89    pub fn periods_in_span(&self, span: TimeSpan) -> u64 {
90        self.periods_in_elements(self.elements(span)).0
91    }
92
93    /// Returns the elements of a single period.
94    #[inline]
95    fn period_elements(&self) -> Elements {
96        Elements(self.cycle.get())
97    }
98
99    /// Returns the elements of given number of periods.
100    #[inline]
101    fn periods_elements(&self, count: u64) -> Elements {
102        Elements(self.cycle.get() * count)
103    }
104
105    /// Returns the number of periods fit in specified elements count.
106    /// And remaining elements.
107    #[inline]
108    fn periods_in_elements(&self, span: Elements) -> (u64, Elements) {
109        let periods = span.0 / self.cycle.get();
110        let remaining = Elements(span.0 % self.cycle.get());
111        (periods, remaining)
112    }
113
114    /// Returns the number of elements until next tick.
115    #[inline]
116    fn until_next(&self, span: Elements) -> Elements {
117        Elements(self.cycle.get() - span.0 % self.cycle)
118    }
119
120    /// Span that contains the given number of frequency elements.
121    #[inline]
122    fn span_fitting_elements(&self, span: Elements) -> Option<TimeSpan> {
123        #![allow(clippy::cast_possible_wrap)]
124
125        match (span.0, self.count) {
126            (0, 0) => Some(TimeSpan::ZERO),
127            (_, 0) => None,
128            (span, count) => {
129                let nanos = span.div_ceil(count);
130                debug_assert!(i64::try_from(nanos).is_ok(), "Nanos overflow");
131                Some(TimeSpan::new(nanos as i64))
132            }
133        }
134    }
135
136    /// Returns new ticker with this frequency and given start time stamp.
137    #[inline]
138    #[must_use]
139    pub fn ticker(&self, start: TimeStamp) -> FrequencyTicker {
140        FrequencyTicker::new(*self, start)
141    }
142}
143
144#[cfg(feature = "serde")]
145impl serde::Serialize for Frequency {
146    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
147        if serializer.is_human_readable() {
148            serializer.serialize_str(&format!("{}/{} Hz", self.count, self.cycle))
149        } else {
150            let mut serializer = serializer.serialize_tuple_struct("Frequency", 2)?;
151            serializer.serialize_field(&self.count)?;
152            serializer.serialize_field(&self.cycle)?;
153            serializer.end()
154        }
155    }
156}
157
158#[cfg(feature = "serde")]
159impl<'de> serde::Deserialize<'de> for Frequency {
160    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
161    where
162        D: serde::Deserializer<'de>,
163    {
164        if deserializer.is_human_readable() {
165            let s = String::deserialize(deserializer)?;
166
167            match s.split_once("/") {
168                None => {
169                    let count = s
170                        .strip_suffix("Hz")
171                        .ok_or_else(|| serde::de::Error::custom("Wrong frequency format"))?;
172                    let count = count.trim();
173                    let count = count.parse().map_err(serde::de::Error::custom)?;
174
175                    let cycle = const { NonZeroU64::new(1).unwrap() };
176                    return Ok(Frequency { count, cycle });
177                }
178
179                Some((count, s)) => {
180                    let count = count.trim();
181                    let count = count.parse().map_err(serde::de::Error::custom)?;
182                    let cycle = s
183                        .strip_suffix("Hz")
184                        .ok_or_else(|| serde::de::Error::custom("Wrong frequency format"))?;
185                    let cycle = cycle.trim();
186                    let cycle = cycle.parse().map_err(serde::de::Error::custom)?;
187
188                    return Ok(Frequency { count, cycle });
189                }
190            }
191        } else {
192            struct FrequencyVisitor;
193
194            impl<'de> serde::de::Visitor<'de> for FrequencyVisitor {
195                type Value = Frequency;
196
197                fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
198                    formatter.write_str("a tuple of 2 elements")
199                }
200
201                fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
202                where
203                    A: serde::de::SeqAccess<'de>,
204                {
205                    let count = seq
206                        .next_element()?
207                        .ok_or_else(|| serde::de::Error::custom("Frequency is empty"))?;
208                    let cycle = seq
209                        .next_element()?
210                        .ok_or_else(|| serde::de::Error::custom("Frequency is empty"))?;
211                    Ok(Frequency { count, cycle })
212                }
213            }
214
215            deserializer.deserialize_tuple_struct("Frequency", 2, FrequencyVisitor)
216        }
217    }
218}
219
220#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
221#[repr(transparent)]
222struct Elements(u64);
223
224impl ops::Add for Elements {
225    type Output = Self;
226
227    #[inline]
228    fn add(self, rhs: Self) -> Self::Output {
229        Elements(self.0 + rhs.0)
230    }
231}
232
233impl ops::AddAssign for Elements {
234    #[inline]
235    fn add_assign(&mut self, rhs: Self) {
236        self.0 += rhs.0;
237    }
238}
239
240impl ops::Sub for Elements {
241    type Output = Self;
242
243    #[inline]
244    fn sub(self, rhs: Self) -> Self::Output {
245        Elements(self.0 - rhs.0)
246    }
247}
248
249impl ops::SubAssign for Elements {
250    #[inline]
251    fn sub_assign(&mut self, rhs: Self) {
252        self.0 -= rhs.0;
253    }
254}
255
256impl ops::Rem for Elements {
257    type Output = Self;
258
259    #[inline]
260    fn rem(self, rhs: Self) -> Self {
261        Elements(self.0 % rhs.0)
262    }
263}
264
265impl ops::RemAssign for Elements {
266    #[inline]
267    fn rem_assign(&mut self, rhs: Self) {
268        self.0 %= rhs.0;
269    }
270}
271
272/// Ticker with the given frequency.
273///
274/// Creates tick iterators on each step that emits `ClockStep`s with exactly given frequency.
275pub struct FrequencyTicker {
276    freq: Frequency,
277
278    /// Number of elements until next tick.
279    until_next: Elements,
280
281    /// Last tick stamp.
282    now: TimeStamp,
283}
284
285impl FrequencyTicker {
286    /// Creates new ticker with given frequency and start timestamp.
287    #[inline]
288    #[must_use]
289    pub fn new(freq: Frequency, now: TimeStamp) -> Self {
290        FrequencyTicker::with_delay(freq, 0, now)
291    }
292
293    /// Creates new ticker with given frequency and delay in number of tick periods.
294    #[inline]
295    #[must_use]
296    pub fn with_delay(freq: Frequency, periods: u64, now: TimeStamp) -> Self {
297        FrequencyTicker {
298            freq,
299            until_next: freq.periods_elements(1 + periods),
300            now,
301        }
302    }
303
304    /// Returns next timestamp when next tick will happen.
305    #[inline]
306    #[must_use]
307    pub fn next_tick(&self) -> Option<TimeStamp> {
308        Some(self.now + self.freq.span_fitting_elements(self.until_next)?)
309    }
310
311    /// Advances ticker forward for `span` and returns iterator over ticks
312    /// since last advancement.
313    #[inline]
314    pub fn ticks(&mut self, step: TimeSpan) -> FrequencyTickerIter {
315        let span = self.freq.elements(step);
316
317        let iter = FrequencyTickerIter {
318            span,
319            freq: self.freq,
320            until_next: self.until_next,
321            accumulated: 0,
322            now: self.now,
323        };
324
325        if span >= self.until_next {
326            self.until_next = self.freq.until_next(span - self.until_next);
327        } else {
328            self.until_next -= span;
329        }
330
331        self.now += step;
332
333        iter
334    }
335
336    /// Advances ticker forward to `now` and returns number of ticks
337    /// since last advancement.
338    #[inline]
339    pub fn tick_count(&mut self, step: TimeSpan) -> u64 {
340        self.ticks(step).ticks()
341    }
342
343    /// Advances ticker forward for `step` and calls provided closure with `ClockStep`s.
344    #[inline]
345    pub fn with_ticks(&mut self, step: TimeSpan, f: impl FnMut(ClockStep)) {
346        self.ticks(step).for_each(f);
347    }
348
349    /// Returns frequency of the ticker.
350    #[inline]
351    #[must_use]
352    pub fn frequency(&self) -> Frequency {
353        self.freq
354    }
355
356    /// Sets new frequency of the ticker.
357    ///
358    /// If `clip_period` is true, then next tick will happen at least in the frequency period.
359    /// If `clip_period` is false, then next tick will happen at the same time as before setting the frequency.
360    #[inline]
361    pub fn set_frequency(&mut self, freq: Frequency, clip_period: bool) {
362        self.freq = freq;
363        if clip_period {
364            let period = freq.period_elements();
365            if self.until_next > period {
366                self.until_next = period;
367            }
368        }
369    }
370}
371
372/// Iterator over ticks from `FrequencyTicker`.
373pub struct FrequencyTickerIter {
374    span: Elements,
375    freq: Frequency,
376    until_next: Elements,
377    accumulated: u64,
378    now: TimeStamp,
379}
380
381impl FrequencyTickerIter {
382    /// Returns number of ticks this iterator will produce.
383    #[inline]
384    #[must_use]
385    pub fn ticks(&self) -> u64 {
386        if self.span < self.until_next {
387            return 0;
388        }
389
390        let span = self.span - self.until_next;
391        1 + self.freq.periods_in_elements(span).0
392    }
393}
394
395impl Iterator for FrequencyTickerIter {
396    type Item = ClockStep;
397
398    #[inline]
399    fn next(&mut self) -> Option<ClockStep> {
400        if self.accumulated > 0 {
401            self.accumulated -= 1;
402            return Some(ClockStep {
403                now: self.now,
404                step: TimeSpan::ZERO,
405            });
406        }
407
408        if self.span < self.until_next {
409            return None;
410        }
411
412        // This may not be None.
413        // If freq.count is 0, then span is 0.
414        // And this may only be called if until_next is 0.
415        // But for better code-gen it uses unwrap_or to not generate panic code.
416        let next = self
417            .freq
418            .span_fitting_elements(self.until_next)
419            .unwrap_or(TimeSpan::ZERO);
420
421        // Advance a whole number of nanoseconds in elements.
422        let advance = self.freq.elements(next);
423
424        debug_assert!(
425            advance <= self.span,
426            "Span cannot be greater than total span left in iterator"
427        );
428        debug_assert!(
429            advance >= self.until_next,
430            "Span cannot be less then span until next tick"
431        );
432
433        let (periods, remaining) = self.freq.periods_in_elements(advance - self.until_next);
434
435        self.accumulated = periods;
436
437        self.until_next = self.freq.period_elements() - remaining;
438
439        self.span -= advance;
440        self.now += next;
441
442        Some(ClockStep {
443            now: self.now,
444            step: next,
445        })
446    }
447}
448
449impl FusedIterator for FrequencyTickerIter {}
450
451/// This trait adds methods to integers to convert values into `Frequency`s.
452pub trait FrequencyNumExt {
453    /// Convert integer value into `Frequency` with that amount of Herz.
454    fn hz(self) -> Frequency;
455
456    /// Convert integer value into `Frequency` with that amount of `KiloHerz`.
457    fn khz(self) -> Frequency;
458
459    /// Convert integer value into `Frequency` with that amount of `MegaHerz`.
460    fn mhz(self) -> Frequency;
461
462    /// Convert integer value into `Frequency` with that amount of `GigaHerz`.
463    fn ghz(self) -> Frequency;
464}
465
466impl FrequencyNumExt for u64 {
467    #[inline]
468    fn hz(self) -> Frequency {
469        Frequency::from_hz(self)
470    }
471
472    #[inline]
473    fn khz(self) -> Frequency {
474        Frequency::from_khz(self)
475    }
476
477    #[inline]
478    fn mhz(self) -> Frequency {
479        Frequency::from_mhz(self)
480    }
481
482    #[inline]
483    fn ghz(self) -> Frequency {
484        Frequency::from_ghz(self)
485    }
486}
487
488#[test]
489fn test_freq_ticker() {
490    use crate::span::TimeSpanNumExt;
491
492    let mut ticker = FrequencyTicker::new(Frequency::new(3, 10.nanoseconds()), TimeStamp::start());
493
494    assert_eq!(ticker.tick_count(10.nanoseconds()), 3);
495
496    let ticks = [0, 0, 0, 1, 0, 0, 1, 0, 0, 1];
497
498    for _ in 0..10 {
499        for tick in ticks {
500            assert_eq!(ticker.tick_count(TimeSpan::NANOSECOND), tick);
501        }
502    }
503}
504
505#[test]
506fn test_freq_ticker_delay() {
507    use crate::span::TimeSpanNumExt;
508
509    const DELAY: u64 = 12;
510
511    let mut ticker = FrequencyTicker::with_delay(
512        Frequency::new(3, 10.nanoseconds()),
513        DELAY,
514        TimeStamp::start(),
515    );
516
517    assert_eq!(0, ticker.tick_count(40.nanoseconds()));
518
519    let ticks = [0, 0, 0, 1, 0, 0, 1, 0, 0, 1];
520
521    for _ in 0..10 {
522        for tick in ticks {
523            assert_eq!(ticker.tick_count(TimeSpan::NANOSECOND), tick);
524        }
525    }
526}
527
528#[test]
529fn test_freq_ticker_next_tick() {
530    use crate::span::TimeSpanNumExt;
531
532    let mut ticker = FrequencyTicker::new(Frequency::new(3, 10.nanoseconds()), TimeStamp::start());
533
534    let ticks = [0, 0, 0, 1, 0, 0, 1, 0, 0, 1];
535
536    let mut next_tick = ticker.next_tick().unwrap();
537
538    for _ in 0..100 {
539        for tick in ticks {
540            assert_eq!(ticker.tick_count(TimeSpan::NANOSECOND), tick);
541
542            if tick > 0 {
543                assert_eq!(next_tick, ticker.now);
544                next_tick = ticker.next_tick().unwrap();
545            } else {
546                assert_eq!(next_tick, ticker.next_tick().unwrap());
547            }
548        }
549    }
550}
551
552#[test]
553fn test_hz() {
554    let mut freq = Frequency::from_hz(3).ticker(TimeStamp::start());
555
556    let ticks = freq.ticks(TimeSpan::SECOND).collect::<Vec<_>>();
557    assert_eq!(
558        ticks,
559        vec![
560            ClockStep {
561                now: TimeStamp::start() + TimeSpan::new(333_333_334),
562                step: TimeSpan::new(333_333_334),
563            },
564            ClockStep {
565                now: TimeStamp::start() + TimeSpan::new(666_666_667),
566                step: TimeSpan::new(333_333_333),
567            },
568            ClockStep {
569                now: TimeStamp::start() + TimeSpan::new(1_000_000_000),
570                step: TimeSpan::new(333_333_333),
571            },
572        ]
573    );
574}