firewheel_core/diff/
notify.rs

1use crate::{
2    diff::{Diff, Patch},
3    event::ParamData,
4};
5use bevy_platform::sync::atomic::{AtomicU64, Ordering};
6
7// Increment an atomic counter.
8//
9// This is guaranteed to never return zero.
10#[inline(always)]
11fn increment_counter() -> u64 {
12    static NOTIFY_COUNTER: AtomicU64 = AtomicU64::new(1);
13
14    NOTIFY_COUNTER
15        .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |current_val| {
16            current_val
17                // Attempt increment
18                .checked_add(1)
19                // If it overflows, return 1 instead
20                .or(Some(1))
21        })
22        // We always return `Some`
23        .unwrap()
24}
25
26/// A lightweight wrapper that guarantees an event
27/// will be generated every time the inner value is accessed mutably,
28/// even if the value doesn't change.
29///
30/// This is useful for types like a play head
31/// where periodically writing the same value
32/// carries useful information.
33///
34/// [`Notify`] implements [`core::ops::Deref`] and [`core::ops::DerefMut`]
35/// for the inner `T`.
36#[derive(Debug, Clone)]
37pub struct Notify<T> {
38    value: T,
39    counter: u64,
40}
41
42impl<T> Notify<T> {
43    /// Construct a new [`Notify`].
44    ///
45    /// If two instances of [`Notify`] are constructed separately,
46    /// a call to [`Diff::diff`] will produce an event, even if the
47    /// value is the same.
48    ///
49    /// ```
50    /// # use firewheel_core::diff::Notify;
51    /// // Diffing `a` and `b` will produce an event
52    /// let a = Notify::new(1);
53    /// let b = Notify::new(1);
54    ///
55    /// // whereas `b` and `c` will not.
56    /// let c = b.clone();
57    /// ```
58    pub fn new(value: T) -> Self {
59        Self {
60            value,
61            counter: increment_counter(),
62        }
63    }
64
65    /// Get this instance's unique ID.
66    ///
67    /// After each mutable dereference, this ID will be replaced
68    /// with a new, unique value. For all practical purposes,
69    /// the ID can be considered unique among all [`Notify`] instances.
70    ///
71    /// [`Notify`] IDs are guaranteed to never be 0, so it can be
72    /// used as a sentinel value.
73    #[inline(always)]
74    pub fn id(&self) -> u64 {
75        self.counter
76    }
77
78    /// Get mutable access to the inner value without updating the ID.
79    pub fn as_mut_unsync(&mut self) -> &mut T {
80        &mut self.value
81    }
82
83    /// Manually update the internal ID without modifying the internals.
84    pub fn notify(&mut self) {
85        self.counter = increment_counter();
86    }
87}
88
89impl<T> AsRef<T> for Notify<T> {
90    fn as_ref(&self) -> &T {
91        &self.value
92    }
93}
94
95impl<T> AsMut<T> for Notify<T> {
96    fn as_mut(&mut self) -> &mut T {
97        self.counter = increment_counter();
98
99        &mut self.value
100    }
101}
102
103impl<T: Default> Default for Notify<T> {
104    fn default() -> Self {
105        Self::new(T::default())
106    }
107}
108
109impl<T> core::ops::Deref for Notify<T> {
110    type Target = T;
111
112    fn deref(&self) -> &Self::Target {
113        &self.value
114    }
115}
116
117impl<T> core::ops::DerefMut for Notify<T> {
118    fn deref_mut(&mut self) -> &mut Self::Target {
119        self.counter = increment_counter();
120
121        &mut self.value
122    }
123}
124
125impl<T: Clone + Send + Sync + 'static> Diff for Notify<T> {
126    fn diff<E: super::EventQueue>(
127        &self,
128        baseline: &Self,
129        path: super::PathBuilder,
130        event_queue: &mut E,
131    ) {
132        if self.counter != baseline.counter {
133            event_queue.push_param(ParamData::any(self.clone()), path);
134        }
135    }
136}
137
138impl<T: Clone + Send + Sync + 'static> Patch for Notify<T> {
139    type Patch = Self;
140
141    fn patch(data: &ParamData, _: &[u32]) -> Result<Self::Patch, super::PatchError> {
142        data.downcast_ref()
143            .ok_or(super::PatchError::InvalidData)
144            .cloned()
145    }
146
147    fn apply(&mut self, patch: Self::Patch) {
148        *self = patch;
149    }
150}
151
152#[cfg(test)]
153mod test {
154    use crate::diff::PathBuilder;
155
156    use super::*;
157
158    #[test]
159    fn test_identical_write() {
160        let baseline = Notify::new(0.5f32);
161        let mut value = baseline.clone();
162
163        let mut events = Vec::new();
164        value.diff(&baseline, PathBuilder::default(), &mut events);
165        assert_eq!(events.len(), 0);
166
167        *value = 0.5f32;
168
169        value.diff(&baseline, PathBuilder::default(), &mut events);
170        assert_eq!(events.len(), 1);
171    }
172}