repr_rs/cache/mod.rs
1pub(crate) mod lazy;
2#[cfg(feature = "eager")]
3pub mod eager;
4
5use crate::Repr;
6use downcast_rs::{impl_downcast, Downcast};
7use std::collections::BTreeMap;
8use std::fmt::{Debug, Display};
9use std::hash::{Hash, Hasher};
10use std::ops::{Deref, DerefMut};
11
12pub(crate) trait Cache<T>: Downcast {
13 fn notify(&self, _value: &T);
14}
15impl_downcast!(Cache<T>);
16
17/// Wraps a value and ensures that an invariant is maintained while allowing that value to be
18/// mutated. The invariant is checked after every mutation.
19/// Additionally, this struct allows for cacheable reads of the value. This is useful when the
20/// read function is expensive. By default, the caching is lazy, so after a value is read once that
21/// same read function will fetch the cached value unless the value has been mutated.
22///
23/// With the feature `eager` enabled, the [`crate::EagerCacheLookup`] trait is implemented for this struct
24/// and can be used to cache values eagerly. Whenever the value is mutated, all eager caches
25/// will be updated in parallel.
26///
27/// This struct requires that the value has a `'static` lifetime. If you need to store a value
28/// with a non-static lifetime consider using [`Repr`].
29pub struct CacheableRepr<T: Debug + 'static, I: Fn(&T) -> bool> {
30 inner: Repr<T, I>,
31 caches: BTreeMap<usize, Box<dyn Cache<T>>>,
32 eager_caches: BTreeMap<usize, Box<dyn Cache<T>>>,
33}
34impl<T: Debug + 'static, I: Fn(&T) -> bool> CacheableRepr<T, I> {
35 /// Creates a new representation invariant with the given value and invariant function.
36 /// ```rust
37 /// use repr_rs::CacheableRepr;
38 /// #[derive(Debug)]
39 /// struct MinMax { min: i32, max: i32 }
40 /// CacheableRepr::new(
41 /// MinMax { min: 1, max: 5 },
42 /// |mm| mm.min < mm.max,
43 /// );
44 /// ```
45 pub const fn new(inner: T, invariant: I) -> Self {
46 let repr = Repr::new(inner, invariant);
47 Self {
48 caches: BTreeMap::new(),
49 eager_caches: BTreeMap::new(),
50 inner: repr,
51 }
52 }
53 /// Creates a new representation invariant with the given value, invariant function, and violation message.
54 /// ```rust
55 /// use repr_rs::CacheableRepr;
56 /// #[derive(Debug)]
57 /// struct MinMax { min: i32, max: i32 }
58 /// CacheableRepr::with_msg(
59 /// MinMax { min: 1, max: 5 },
60 /// |mm| mm.min < mm.max,
61 /// "min must be less than max",
62 /// );
63 /// ```
64 pub const fn with_msg(inner: T, invariant: I, violation_message: &'static str) -> Self {
65 let repr = Repr::with_msg(inner, invariant, violation_message);
66 Self {
67 caches: BTreeMap::new(),
68 eager_caches: BTreeMap::new(),
69 inner: repr,
70 }
71 }
72 /// Borrows a read-only view of the value in the representation invariant.
73 /// ```rust
74 /// use repr_rs::CacheableRepr;
75 /// #[derive(Debug)]
76 /// struct MinMax { min: i32, max: i32 }
77 /// let repr = CacheableRepr::new(MinMax { min: 1, max: 5 }, |mm| mm.min < mm.max);
78 /// let view = repr.read();
79 /// assert_eq!(1, view.min);
80 /// assert_eq!(5, view.max);
81 /// ```
82 #[inline]
83 pub fn read(&self) -> &T {
84 // Safety: borrowing rules ensure that T is valid, and because this is an immutable borrow
85 // of the Repr, no mutable borrows can take place.
86 self.inner.read()
87 }
88 /// Borrows a mutable view of the value in the representation invariant.
89 /// ```rust
90 /// use repr_rs::CacheableRepr;
91 /// #[derive(Debug)]
92 /// struct MinMax { min: i32, max: i32 }
93 /// let mut repr = CacheableRepr::new(MinMax { min: 1, max: 5 }, |mm| mm.min < mm.max);
94 /// {
95 /// let view = repr.read();
96 /// assert_eq!(1, view.min);
97 /// assert_eq!(5, view.max);
98 /// }
99 /// repr.write().min = 4;
100 /// let view = repr.read();
101 /// assert_eq!(4, view.min);
102 /// assert_eq!(5, view.max);
103 /// ```
104 ///
105 /// Rust's borrowing rules prevent the read-only view being held while a mutation occurs. For
106 /// example, this won't compile:
107 /// ```compile_fail
108 /// use repr_rs::CacheableRepr;
109 /// #[derive(Debug)]
110 /// struct MinMax { min: i32, max: i32 }
111 /// let mut repr = CacheableRepr::new(MinMax { min: 1, max: 5 }, |mm| mm.min < mm.max);
112 /// let view = repr.read();
113 /// assert_eq!(1, view.min);
114 /// assert_eq!(5, view.max);
115 /// // error[E0502]: cannot borrow `repr` as mutable because it is also borrowed as immutable
116 /// repr.write().min = 4;
117 /// assert_eq!(4, view.min);
118 /// assert_eq!(5, view.max);
119 /// ```
120 #[inline]
121 pub fn write(&mut self) -> ReprMutator<T, I> {
122 // Can be `const` when `const_mut_refs` is stabilised.
123 ReprMutator {
124 repr: self,
125 }
126 }
127 /// Consumes the representation invariant and returns the inner value.
128 /// ```rust
129 /// use repr_rs::Repr;
130 /// #[derive(Debug)]
131 /// struct MinMax { min: i32, max: i32 }
132 /// let repr = Repr::new(MinMax { min: 1, max: 5 }, |mm| mm.min < mm.max);
133 /// let inner = repr.into_inner();
134 /// assert_eq!(1, inner.min);
135 /// ```
136 #[inline]
137 pub fn into_inner(self) -> T {
138 self.inner.into_inner()
139 }
140 /// Borrows a read-only view of the value in the representation invariant and caches the
141 /// result of the read function. The cache is keyed by the read function's address, so in general
142 /// you should use function references instead of closures. It is a bug to perform any side effects
143 /// in the read function (i.e. reading from a file).
144 /// ```rust
145 /// use std::sync::atomic::{AtomicU32, Ordering};
146 /// use repr_rs::CacheableRepr;
147 /// #[derive(Debug)]
148 /// struct Person { name: String }
149 /// let mut repr = CacheableRepr::new(Person { name: "Alice and Bob together at last".into() }, |p| !p.name.is_empty());
150 /// static READ_SPY: AtomicU32 = AtomicU32::new(0);
151 /// fn expensive_read(p: &Person) -> usize {
152 /// // Just for demonstration purposes.
153 /// // Do not do side effects in your read functions!
154 /// READ_SPY.fetch_add(1, Ordering::Relaxed);
155 /// fib(p.name.len())
156 /// }
157 /// let fib_of_name_len = repr.lazy(expensive_read);
158 /// assert_eq!(832040, fib_of_name_len);
159 /// // this does not recompute the fibonacci number, it just gets it from the cache!
160 /// let fib_of_name_len2 = repr.lazy(expensive_read);
161 /// assert_eq!(832040, fib_of_name_len2);
162 /// repr.write().name = "Alice".into();
163 /// // this recomputes the fibonacci number because the name has changed
164 /// let fib_of_name_len3 = repr.lazy(expensive_read);
165 /// assert_eq!(5, fib_of_name_len3);
166 /// assert_eq!(2, READ_SPY.load(Ordering::Relaxed));
167 /// # fn fib(n: usize) -> usize {
168 /// # if n <= 1 { n } else { fib(n - 1) + fib(n - 2) }
169 /// # }
170 pub fn lazy<R: Clone + 'static>(&mut self, read_fn: fn(&T) -> R) -> R {
171 let fn_identity = read_fn as *const fn(&T) -> R as usize;
172 let entry = self.caches.entry(fn_identity);
173
174 let cache = entry.or_insert_with(|| Box::new(lazy::CacheableRead::<T, R>::new(read_fn)));
175 let cache = cache.downcast_mut::<lazy::CacheableRead<T, R>>().unwrap();
176 let data = self.inner.inner.get_mut();
177 cache.read(data)
178 }
179
180 fn check(&mut self) {
181 self.inner.check();
182 let data = self.inner.inner.get_mut();
183 for cache in self.caches.values().chain(self.eager_caches.values()) {
184 cache.notify(data);
185 }
186 }
187}
188impl<T: Debug + 'static, I: Fn(&T) -> bool> From<Repr<T, I>> for CacheableRepr<T, I> {
189 fn from(value: Repr<T, I>) -> Self {
190 Self {
191 caches: BTreeMap::new(),
192 eager_caches: BTreeMap::new(),
193 inner: value,
194 }
195 }
196}
197impl<T: Debug + 'static, I: Fn(&T) -> bool> From<CacheableRepr<T, I>> for Repr<T, I> {
198 fn from(value: CacheableRepr<T, I>) -> Self {
199 value.inner
200 }
201}
202/// # Safety
203/// This is safe because we can only mutate the inner value through the ReprMutator, which can only
204/// be created by borrowing the Repr mutably. The only other potential issue could be if the
205/// invariant function was not thread safe, which is why we require it to be [Sync].
206///
207/// The inner mutation (i.e. adding new caches or updating caches) either requires a mutable borrow
208/// or is guarded behind a lock.
209unsafe impl<T: Debug + Sync, I: Fn(&T) -> bool + Sync> Sync for CacheableRepr<T, I> {}
210/// # Safety
211/// We exclusively own all inner values here (both the repr and the caches), so we can safely
212/// implement Send for this type.
213unsafe impl<T: Debug + Send, I: Fn(&T) -> bool + Send> Send for CacheableRepr<T, I> {}
214impl<T: Debug, I: Fn(&T) -> bool> AsRef<T> for CacheableRepr<T, I> {
215 fn as_ref(&self) -> &T {
216 self.read()
217 }
218}
219impl<T: Debug + Clone, I: Fn(&T) -> bool + Clone> Clone for CacheableRepr<T, I> {
220 fn clone(&self) -> Self {
221 let clone = self.inner.clone();
222 Self::from(clone)
223 }
224}
225impl<T: Debug + Hash, I: Fn(&T) -> bool> Hash for CacheableRepr<T, I> {
226 fn hash<H: Hasher>(&self, state: &mut H) {
227 self.inner.hash(state);
228 }
229}
230impl<T: Debug + PartialEq, I: Fn(&T) -> bool> PartialEq for CacheableRepr<T, I> {
231 fn eq(&self, other: &Self) -> bool {
232 self.inner.eq(&other.inner)
233 }
234}
235impl<T: Debug + Eq, I: Fn(&T) -> bool> Eq for CacheableRepr<T, I> {}
236
237impl<T: Debug, I: Fn(&T) -> bool> Debug for CacheableRepr<T, I> {
238 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
239 write!(f, "Repr({:?})", self.read())
240 }
241}
242impl <T: Debug + Display, I: Fn(&T) -> bool> Display for CacheableRepr<T, I> {
243 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
244 write!(f, "{}", self.read())
245 }
246}
247
248#[repr(transparent)]
249pub struct ReprMutator<'a, T: Debug + 'static, I: Fn(&T) -> bool> {
250 // inner: &'a mut T,
251 repr: &'a mut CacheableRepr<T, I>,
252}
253impl<'a, T: Debug, I: Fn(&T) -> bool> Deref for ReprMutator<'a, T, I> {
254 type Target = T;
255 fn deref(&self) -> &Self::Target {
256 // Safety: borrowing rules ensure that T is valid, and because ReprMutate mutably borrows
257 // the Repr, no mutable borrows of the inner can take place if we borrow it as imm here.
258 unsafe { &*self.repr.inner.inner.get() }
259 }
260}
261impl<'a, T: Debug, I: Fn(&T) -> bool> AsRef<T> for ReprMutator<'a, T, I> {
262 fn as_ref(&self) -> &T {
263 self.deref()
264 }
265}
266impl<'a, T: Debug, I: Fn(&T) -> bool> DerefMut for ReprMutator<'a, T, I> {
267 fn deref_mut(&mut self) -> &mut Self::Target {
268 self.repr.inner.inner.get_mut()
269 }
270}
271impl<'a, T: Debug, I: Fn(&T) -> bool> AsMut<T> for ReprMutator<'a, T, I> {
272 fn as_mut(&mut self) -> &mut T {
273 self.deref_mut()
274 }
275}
276impl<T: Debug, I: Fn(&T) -> bool> Drop for ReprMutator<'_, T, I> {
277 fn drop(&mut self) {
278 self.repr.check();
279 }
280}
281
282// For Deref/DerefMut we need to make sure that it hashes, orders, and has equality with the
283// same semantics as the reference we give
284impl<'a, T: Debug + Hash> Hash for ReprMutator<'a, T, fn(&T) -> bool> {
285 fn hash<H: Hasher>(&self, state: &mut H) {
286 self.deref().hash(state);
287 }
288}
289impl<'a, T: Debug + PartialEq> PartialEq for ReprMutator<'a, T, fn(&T) -> bool> {
290 fn eq(&self, other: &Self) -> bool {
291 self.deref() == other.deref()
292 }
293}
294impl<'a, T: Debug + Eq> Eq for ReprMutator<'a, T, fn(&T) -> bool> {}
295impl<'a, T: Debug + PartialOrd> PartialOrd for ReprMutator<'a, T, fn(&T) -> bool> {
296 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
297 self.deref().partial_cmp(other.deref())
298 }
299}
300impl<'a, T: Debug + Ord> Ord for ReprMutator<'a, T, fn(&T) -> bool> {
301 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
302 self.deref().cmp(other.deref())
303 }
304}